Index: head/sys/dev/iwm/if_iwm.c =================================================================== --- head/sys/dev/iwm/if_iwm.c (revision 303326) +++ head/sys/dev/iwm/if_iwm.c (revision 303327) @@ -1,5077 +1,6189 @@ -/* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.42 2015/05/30 02:49:23 deraadt Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include const uint8_t iwm_nvm_channels[] = { /* 2.4 GHz */ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 5 GHz */ 36, 40, 44, 48, 52, 56, 60, 64, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 149, 153, 157, 161, 165 }; -#define IWM_NUM_2GHZ_CHANNELS 14 - _Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS, "IWM_NUM_CHANNELS is too small"); +const uint8_t iwm_nvm_channels_8000[] = { + /* 2.4 GHz */ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + /* 5 GHz */ + 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, + 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, + 149, 153, 157, 161, 165, 169, 173, 177, 181 +}; +_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000, + "IWM_NUM_CHANNELS_8000 is too small"); + +#define IWM_NUM_2GHZ_CHANNELS 14 +#define IWM_N_HW_ADDR_MASK 0xF + /* * XXX For now, there's simply a fixed set of rate table entries * that are populated. */ const struct iwm_rate { uint8_t rate; uint8_t plcp; } iwm_rates[] = { { 2, IWM_RATE_1M_PLCP }, { 4, IWM_RATE_2M_PLCP }, { 11, IWM_RATE_5M_PLCP }, { 22, IWM_RATE_11M_PLCP }, { 12, IWM_RATE_6M_PLCP }, { 18, IWM_RATE_9M_PLCP }, { 24, IWM_RATE_12M_PLCP }, { 36, IWM_RATE_18M_PLCP }, { 48, IWM_RATE_24M_PLCP }, { 72, IWM_RATE_36M_PLCP }, { 96, IWM_RATE_48M_PLCP }, { 108, IWM_RATE_54M_PLCP }, }; #define IWM_RIDX_CCK 0 #define IWM_RIDX_OFDM 4 #define IWM_RIDX_MAX (nitems(iwm_rates)-1) #define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) #define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) +struct iwm_nvm_section { + uint16_t length; + uint8_t *data; +}; + static int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); static int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type, const uint8_t *, size_t); static int iwm_set_default_calib(struct iwm_softc *, const void *); static void iwm_fw_info_free(struct iwm_fw_info *); static int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); static void iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, bus_size_t, bus_size_t); static void iwm_dma_contig_free(struct iwm_dma_info *); static int iwm_alloc_fwmem(struct iwm_softc *); static void iwm_free_fwmem(struct iwm_softc *); static int iwm_alloc_sched(struct iwm_softc *); static void iwm_free_sched(struct iwm_softc *); static int iwm_alloc_kw(struct iwm_softc *); static void iwm_free_kw(struct iwm_softc *); static int iwm_alloc_ict(struct iwm_softc *); static void iwm_free_ict(struct iwm_softc *); static int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); static void iwm_disable_rx_dma(struct iwm_softc *); static void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); static void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); static int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, int); static void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); static void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); static void iwm_enable_interrupts(struct iwm_softc *); static void iwm_restore_interrupts(struct iwm_softc *); static void iwm_disable_interrupts(struct iwm_softc *); static void iwm_ict_reset(struct iwm_softc *); static int iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *); static void iwm_stop_device(struct iwm_softc *); 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 void iwm_enable_txq(struct iwm_softc *, int, int); +static int iwm_enable_txq(struct iwm_softc *, int, int, int); static int iwm_post_alive(struct iwm_softc *); static int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, uint16_t, uint8_t *, uint16_t *); static int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, - uint16_t *); + uint16_t *, size_t); static uint32_t iwm_eeprom_channel_flags(uint16_t); static void iwm_add_channel_band(struct iwm_softc *, - struct ieee80211_channel[], int, int *, int, int, + struct ieee80211_channel[], int, int *, int, size_t, const uint8_t[]); static void iwm_init_channel_map(struct ieee80211com *, int, int *, struct ieee80211_channel[]); static int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, - const uint16_t *, const uint16_t *, uint8_t, - uint8_t); -struct iwm_nvm_section; + const uint16_t *, const uint16_t *, + const uint16_t *, const uint16_t *, + const uint16_t *); +static void iwm_set_hw_address_8000(struct iwm_softc *, + struct iwm_nvm_data *, + const uint16_t *, const uint16_t *); +static int iwm_get_sku(const struct iwm_softc *, const uint16_t *, + const uint16_t *); +static int iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *); +static int iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *, + const uint16_t *); +static int iwm_get_n_hw_addrs(const struct iwm_softc *, + const uint16_t *); +static void iwm_set_radio_cfg(const struct iwm_softc *, + struct iwm_nvm_data *, uint32_t); static int iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); static int iwm_nvm_init(struct iwm_softc *); +static int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, + const uint8_t *, uint32_t); static int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, const uint8_t *, uint32_t); +static int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); +static int iwm_load_cpu_sections_8000(struct iwm_softc *, + struct iwm_fw_sects *, int , int *); +static int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); static int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); static int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); static int iwm_send_phy_cfg_cmd(struct iwm_softc *); static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, enum iwm_ucode_type); static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); static int iwm_rx_addbuf(struct iwm_softc *, int, int); static int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); static int iwm_mvm_get_signal_strength(struct iwm_softc *, struct iwm_rx_phy_info *); static void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); static int iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *); static void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); static int iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_node *); static void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); static void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); #if 0 static void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, uint16_t); #endif static const struct iwm_rate * iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, struct ieee80211_frame *, struct iwm_tx_cmd *); 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 void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, - struct iwm_mvm_add_sta_cmd_v5 *); static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, - struct iwm_mvm_add_sta_cmd_v6 *, + struct iwm_mvm_add_sta_cmd_v7 *, 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_node *); static int iwm_auth(struct ieee80211vap *, struct iwm_softc *); static int iwm_assoc(struct ieee80211vap *, struct iwm_softc *); static int iwm_release(struct iwm_softc *, struct iwm_node *); static struct ieee80211_node * iwm_node_alloc(struct ieee80211vap *, const uint8_t[IEEE80211_ADDR_LEN]); static void iwm_setrates(struct iwm_softc *, struct iwm_node *); static int iwm_media_change(struct ifnet *); static int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void iwm_endscan_cb(void *, int); +static void iwm_mvm_fill_sf_command(struct iwm_softc *, + struct iwm_sf_cfg_cmd *, + struct ieee80211_node *); +static int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); +static int iwm_send_bt_init_conf(struct iwm_softc *); +static int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); +static void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); static int iwm_init_hw(struct iwm_softc *); static void iwm_init(struct iwm_softc *); static void iwm_start(struct iwm_softc *); static void iwm_stop(struct iwm_softc *); static void iwm_watchdog(void *); static void iwm_parent(struct ieee80211com *); #ifdef IWM_DEBUG static const char * iwm_desc_lookup(uint32_t); static void iwm_nic_error(struct iwm_softc *); +static void iwm_nic_umac_error(struct iwm_softc *); #endif static void iwm_notif_intr(struct iwm_softc *); static void iwm_intr(void *); static int iwm_attach(device_t); +static int iwm_is_valid_ether_addr(uint8_t *); static void iwm_preinit(void *); static int iwm_detach_local(struct iwm_softc *sc, int); static void iwm_init_task(void *); static void iwm_radiotap_attach(struct iwm_softc *); static struct ieee80211vap * iwm_vap_create(struct ieee80211com *, const char [IFNAMSIZ], int, enum ieee80211_opmode, int, const uint8_t [IEEE80211_ADDR_LEN], const uint8_t [IEEE80211_ADDR_LEN]); static void iwm_vap_delete(struct ieee80211vap *); static void iwm_scan_start(struct ieee80211com *); static void iwm_scan_end(struct ieee80211com *); static void iwm_update_mcast(struct ieee80211com *); static void iwm_set_channel(struct ieee80211com *); static void iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long); static void iwm_scan_mindwell(struct ieee80211_scan_state *); static int iwm_detach(device_t); /* * Firmware parser. */ static int iwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen) { const struct iwm_fw_cscheme_list *l = (const void *)data; if (dlen < sizeof(*l) || dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) return EINVAL; /* we don't actually store anything for now, always use s/w crypto */ return 0; } static int iwm_firmware_store_section(struct iwm_softc *sc, enum iwm_ucode_type type, const uint8_t *data, size_t dlen) { struct iwm_fw_sects *fws; struct iwm_fw_onesect *fwone; if (type >= IWM_UCODE_TYPE_MAX) return EINVAL; if (dlen < sizeof(uint32_t)) return EINVAL; fws = &sc->sc_fw.fw_sects[type]; if (fws->fw_count >= IWM_UCODE_SECT_MAX) return EINVAL; fwone = &fws->fw_sect[fws->fw_count]; /* first 32bit are device load offset */ memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); /* rest is data */ fwone->fws_data = data + sizeof(uint32_t); fwone->fws_len = dlen - sizeof(uint32_t); fws->fw_count++; fws->fw_totlen += fwone->fws_len; return 0; } /* iwlwifi: iwl-drv.c */ struct iwm_tlv_calib_data { uint32_t ucode_type; struct iwm_tlv_calib_ctrl calib; } __packed; static int iwm_set_default_calib(struct iwm_softc *sc, const void *data) { const struct iwm_tlv_calib_data *def_calib = data; uint32_t ucode_type = le32toh(def_calib->ucode_type); if (ucode_type >= IWM_UCODE_TYPE_MAX) { device_printf(sc->sc_dev, "Wrong ucode_type %u for default " "calibration.\n", ucode_type); return EINVAL; } sc->sc_default_calib[ucode_type].flow_trigger = def_calib->calib.flow_trigger; sc->sc_default_calib[ucode_type].event_trigger = def_calib->calib.event_trigger; return 0; } static void iwm_fw_info_free(struct iwm_fw_info *fw) { firmware_put(fw->fw_fp, FIRMWARE_UNLOAD); fw->fw_fp = NULL; /* don't touch fw->fw_status */ memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); } static int iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { struct iwm_fw_info *fw = &sc->sc_fw; const struct iwm_tlv_ucode_header *uhdr; struct iwm_ucode_tlv tlv; enum iwm_ucode_tlv_type tlv_type; const struct firmware *fwp; const uint8_t *data; int error = 0; size_t len; if (fw->fw_status == IWM_FW_STATUS_DONE && ucode_type != IWM_UCODE_TYPE_INIT) return 0; while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); fw->fw_status = IWM_FW_STATUS_INPROGRESS; if (fw->fw_fp != NULL) iwm_fw_info_free(fw); /* * Load firmware into driver memory. * fw_fp will be set. */ IWM_UNLOCK(sc); fwp = firmware_get(sc->sc_fwname); IWM_LOCK(sc); if (fwp == NULL) { device_printf(sc->sc_dev, "could not read firmware %s (error %d)\n", sc->sc_fwname, error); goto out; } fw->fw_fp = fwp; + /* (Re-)Initialize default values. */ + sc->sc_capaflags = 0; + sc->sc_capa_n_scan_channels = IWM_MAX_NUM_SCAN_CHANNELS; + memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); + memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); + /* * Parse firmware contents */ uhdr = (const void *)fw->fw_fp->data; if (*(const uint32_t *)fw->fw_fp->data != 0 || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { device_printf(sc->sc_dev, "invalid firmware %s\n", sc->sc_fwname); error = EINVAL; goto out; } - sc->sc_fwver = le32toh(uhdr->ver); + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", + IWM_UCODE_MAJOR(le32toh(uhdr->ver)), + IWM_UCODE_MINOR(le32toh(uhdr->ver)), + IWM_UCODE_API(le32toh(uhdr->ver))); data = uhdr->data; len = fw->fw_fp->datasize - sizeof(*uhdr); while (len >= sizeof(tlv)) { size_t tlv_len; const void *tlv_data; memcpy(&tlv, data, sizeof(tlv)); tlv_len = le32toh(tlv.length); tlv_type = le32toh(tlv.type); len -= sizeof(tlv); data += sizeof(tlv); tlv_data = data; if (len < tlv_len) { device_printf(sc->sc_dev, "firmware too short: %zu bytes\n", len); error = EINVAL; goto parse_out; } switch ((int)tlv_type) { case IWM_UCODE_TLV_PROBE_MAX_LEN: if (tlv_len < sizeof(uint32_t)) { device_printf(sc->sc_dev, "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n", __func__, (int) tlv_len); error = EINVAL; goto parse_out; } sc->sc_capa_max_probe_len = le32toh(*(const uint32_t *)tlv_data); /* limit it to something sensible */ - if (sc->sc_capa_max_probe_len > (1<<16)) { + if (sc->sc_capa_max_probe_len > + IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " "ridiculous\n", __func__); error = EINVAL; goto parse_out; } break; case IWM_UCODE_TLV_PAN: if (tlv_len) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", __func__, (int) tlv_len); error = EINVAL; goto parse_out; } sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; break; case IWM_UCODE_TLV_FLAGS: if (tlv_len < sizeof(uint32_t)) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", __func__, (int) tlv_len); error = EINVAL; goto parse_out; } /* * Apparently there can be many flags, but Linux driver * parses only the first one, and so do we. * * XXX: why does this override IWM_UCODE_TLV_PAN? * Intentional or a bug? Observations from * current firmware file: * 1) TLV_PAN is parsed first * 2) TLV_FLAGS contains TLV_FLAGS_PAN * ==> this resets TLV_PAN to itself... hnnnk */ sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data); break; case IWM_UCODE_TLV_CSCHEME: if ((error = iwm_store_cscheme(sc, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: iwm_store_cscheme(): returned %d\n", __func__, error); goto parse_out; } break; - case IWM_UCODE_TLV_NUM_OF_CPU: + case IWM_UCODE_TLV_NUM_OF_CPU: { + uint32_t num_cpu; if (tlv_len != sizeof(uint32_t)) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", __func__, (int) tlv_len); error = EINVAL; goto parse_out; } - if (le32toh(*(const uint32_t*)tlv_data) != 1) { + num_cpu = le32toh(*(const uint32_t *)tlv_data); + if (num_cpu < 1 || num_cpu > 2) { device_printf(sc->sc_dev, - "%s: driver supports " - "only TLV_NUM_OF_CPU == 1", + "%s: Driver supports only 1 or 2 CPUs\n", __func__); error = EINVAL; goto parse_out; } break; + } case IWM_UCODE_TLV_SEC_RT: if ((error = iwm_firmware_store_section(sc, IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n", __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_SEC_INIT: if ((error = iwm_firmware_store_section(sc, IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n", __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_SEC_WOWLAN: if ((error = iwm_firmware_store_section(sc, IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n", __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_DEF_CALIB: if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", __func__, (int) tlv_len, (int) sizeof(struct iwm_tlv_calib_data)); error = EINVAL; goto parse_out; } if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { device_printf(sc->sc_dev, "%s: iwm_set_default_calib() failed: %d\n", __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_PHY_SKU: if (tlv_len != sizeof(uint32_t)) { error = EINVAL; device_printf(sc->sc_dev, "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", __func__, (int) tlv_len); goto parse_out; } sc->sc_fw_phy_config = le32toh(*(const uint32_t *)tlv_data); break; - case IWM_UCODE_TLV_API_CHANGES_SET: - case IWM_UCODE_TLV_ENABLED_CAPABILITIES: + case IWM_UCODE_TLV_API_CHANGES_SET: { + const struct iwm_ucode_api *api; + if (tlv_len != sizeof(*api)) { + error = EINVAL; + goto parse_out; + } + api = (const struct iwm_ucode_api *)tlv_data; + /* Flags may exceed 32 bits in future firmware. */ + if (le32toh(api->api_index) > 0) { + device_printf(sc->sc_dev, + "unsupported API index %d\n", + le32toh(api->api_index)); + goto parse_out; + } + sc->sc_ucode_api = le32toh(api->api_flags); + break; + } + + case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { + const struct iwm_ucode_capa *capa; + int idx, i; + if (tlv_len != sizeof(*capa)) { + error = EINVAL; + goto parse_out; + } + capa = (const struct iwm_ucode_capa *)tlv_data; + idx = le32toh(capa->api_index); + if (idx > howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { + device_printf(sc->sc_dev, + "unsupported API index %d\n", idx); + goto parse_out; + } + for (i = 0; i < 32; i++) { + if ((le32toh(capa->api_capa) & (1U << i)) == 0) + continue; + setbit(sc->sc_enabled_capa, i + (32 * idx)); + } + break; + } + + case 48: /* undocumented TLV */ + case IWM_UCODE_TLV_SDIO_ADMA_ADDR: + case IWM_UCODE_TLV_FW_GSCAN_CAPA: /* ignore, not used by current driver */ break; + case IWM_UCODE_TLV_SEC_RT_USNIFFER: + if ((error = iwm_firmware_store_section(sc, + IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, + tlv_len)) != 0) + goto parse_out; + break; + + case IWM_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(uint32_t)) { + error = EINVAL; + goto parse_out; + } + sc->sc_capa_n_scan_channels = + le32toh(*(const uint32_t *)tlv_data); + break; + + case IWM_UCODE_TLV_FW_VERSION: + if (tlv_len != sizeof(uint32_t) * 3) { + error = EINVAL; + goto parse_out; + } + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), + "%d.%d.%d", + le32toh(((const uint32_t *)tlv_data)[0]), + le32toh(((const uint32_t *)tlv_data)[1]), + le32toh(((const uint32_t *)tlv_data)[2])); + break; + default: device_printf(sc->sc_dev, "%s: unknown firmware section %d, abort\n", __func__, tlv_type); error = EINVAL; goto parse_out; } len -= roundup(tlv_len, 4); data += roundup(tlv_len, 4); } KASSERT(error == 0, ("unhandled error")); parse_out: if (error) { device_printf(sc->sc_dev, "firmware parse error %d, " "section type %d\n", error, tlv_type); } if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { device_printf(sc->sc_dev, "device uses unsupported power ops\n"); error = ENOTSUP; } out: if (error) { fw->fw_status = IWM_FW_STATUS_NONE; if (fw->fw_fp != NULL) iwm_fw_info_free(fw); } else fw->fw_status = IWM_FW_STATUS_DONE; wakeup(&sc->sc_fw); return error; } /* * DMA resource routines */ static void iwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { if (error != 0) return; KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); - *(bus_addr_t *)arg = segs[0].ds_addr; + *(bus_addr_t *)arg = segs[0].ds_addr; } static int iwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, bus_size_t size, bus_size_t alignment) { int error; dma->tag = NULL; + dma->map = NULL; dma->size = size; dma->vaddr = NULL; error = bus_dma_tag_create(tag, alignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &dma->tag); if (error != 0) goto fail; error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); if (error != 0) goto fail; error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); if (error != 0) { bus_dmamem_free(dma->tag, dma->vaddr, dma->map); dma->vaddr = NULL; - goto fail; + goto fail; } bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); return 0; -fail: iwm_dma_contig_free(dma); +fail: + iwm_dma_contig_free(dma); + return error; } static void iwm_dma_contig_free(struct iwm_dma_info *dma) { if (dma->vaddr != NULL) { bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, dma->vaddr, dma->map); dma->vaddr = NULL; } if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); dma->tag = NULL; } - } /* fwmem is used to load firmware onto the card */ static int iwm_alloc_fwmem(struct iwm_softc *sc) { /* Must be aligned on a 16-byte boundary. */ return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, sc->sc_fwdmasegsz, 16); } static void iwm_free_fwmem(struct iwm_softc *sc) { iwm_dma_contig_free(&sc->fw_dma); } /* tx scheduler rings. not used? */ static int iwm_alloc_sched(struct iwm_softc *sc) { int rv; /* TX scheduler rings must be aligned on a 1KB boundary. */ rv = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); return rv; } static void iwm_free_sched(struct iwm_softc *sc) { iwm_dma_contig_free(&sc->sched_dma); } /* keep-warm page is used internally by the card. see iwl-fh.h for more info */ static int iwm_alloc_kw(struct iwm_softc *sc) { return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); } static void iwm_free_kw(struct iwm_softc *sc) { iwm_dma_contig_free(&sc->kw_dma); } /* interrupt cause table */ static int iwm_alloc_ict(struct iwm_softc *sc) { return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, IWM_ICT_SIZE, 1<ict_dma); } static int iwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) { bus_size_t size; int i, error; ring->cur = 0; /* Allocate RX descriptors (256-byte aligned). */ size = IWM_RX_RING_COUNT * sizeof(uint32_t); error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); if (error != 0) { device_printf(sc->sc_dev, "could not allocate RX ring DMA memory\n"); goto fail; } ring->desc = ring->desc_dma.vaddr; /* Allocate RX status area (16-byte aligned). */ error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, sizeof(*ring->stat), 16); if (error != 0) { device_printf(sc->sc_dev, "could not allocate RX status DMA memory\n"); goto fail; } ring->stat = ring->stat_dma.vaddr; /* Create RX buffer DMA tag. */ error = bus_dma_tag_create(sc->sc_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create RX buf DMA tag, error %d\n", __func__, error); goto fail; } /* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */ error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } /* * Allocate and map RX buffers. */ for (i = 0; i < IWM_RX_RING_COUNT; i++) { struct iwm_rx_data *data = &ring->data[i]; error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } data->m = NULL; if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) { goto fail; } } return 0; fail: iwm_free_rx_ring(sc, ring); return error; } static void iwm_disable_rx_dma(struct iwm_softc *sc) { - + /* XXX conditional nic locks are stupid */ /* XXX print out if we can't lock the NIC? */ if (iwm_nic_lock(sc)) { /* XXX handle if RX stop doesn't finish? */ (void) iwm_pcie_rx_stop(sc); iwm_nic_unlock(sc); } } static void iwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) { /* Reset the ring state */ ring->cur = 0; + + /* + * The hw rx ring index in shared memory must also be cleared, + * otherwise the discrepancy can cause reprocessing chaos. + */ memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); } static void iwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) { int i; iwm_dma_contig_free(&ring->desc_dma); iwm_dma_contig_free(&ring->stat_dma); for (i = 0; i < IWM_RX_RING_COUNT; i++) { struct iwm_rx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->map != NULL) { bus_dmamap_destroy(ring->data_dmat, data->map); data->map = NULL; } } if (ring->spare_map != NULL) { bus_dmamap_destroy(ring->data_dmat, ring->spare_map); ring->spare_map = NULL; } if (ring->data_dmat != NULL) { bus_dma_tag_destroy(ring->data_dmat); ring->data_dmat = NULL; } } static int iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) { bus_addr_t paddr; bus_size_t size; size_t maxsize; int nsegments; int i, error; ring->qid = qid; ring->queued = 0; ring->cur = 0; /* Allocate TX descriptors (256-byte aligned). */ size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd); error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); if (error != 0) { device_printf(sc->sc_dev, "could not allocate TX ring DMA memory\n"); goto fail; } ring->desc = ring->desc_dma.vaddr; /* * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need * to allocate commands space for other rings. */ if (qid > IWM_MVM_CMD_QUEUE) return 0; size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd); error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4); if (error != 0) { device_printf(sc->sc_dev, "could not allocate TX cmd DMA memory\n"); goto fail; } ring->cmd = ring->cmd_dma.vaddr; /* FW commands may require more mapped space than packets. */ if (qid == IWM_MVM_CMD_QUEUE) { maxsize = IWM_RBUF_SIZE; nsegments = 1; } else { maxsize = MCLBYTES; nsegments = IWM_MAX_SCATTER - 2; } error = bus_dma_tag_create(sc->sc_dmat, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); goto fail; } paddr = ring->cmd_dma.paddr; for (i = 0; i < IWM_TX_RING_COUNT; i++) { struct iwm_tx_data *data = &ring->data[i]; data->cmd_paddr = paddr; data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) + offsetof(struct iwm_tx_cmd, scratch); paddr += sizeof(struct iwm_device_cmd); error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, "could not create TX buf DMA map\n"); goto fail; } } KASSERT(paddr == ring->cmd_dma.paddr + size, ("invalid physical address")); return 0; fail: iwm_free_tx_ring(sc, ring); return error; } static void iwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) { int i; for (i = 0; i < IWM_TX_RING_COUNT; i++) { struct iwm_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } } /* Clear TX descriptors. */ memset(ring->desc, 0, ring->desc_dma.size); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); sc->qfullmsk &= ~(1 << ring->qid); ring->queued = 0; ring->cur = 0; } static void iwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) { int i; iwm_dma_contig_free(&ring->desc_dma); iwm_dma_contig_free(&ring->cmd_dma); for (i = 0; i < IWM_TX_RING_COUNT; i++) { struct iwm_tx_data *data = &ring->data[i]; if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } if (data->map != NULL) { bus_dmamap_destroy(ring->data_dmat, data->map); data->map = NULL; } } if (ring->data_dmat != NULL) { bus_dma_tag_destroy(ring->data_dmat); ring->data_dmat = NULL; } } /* * High-level hardware frobbing routines */ static void iwm_enable_interrupts(struct iwm_softc *sc) { sc->sc_intmask = IWM_CSR_INI_SET_MASK; IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); } static void iwm_restore_interrupts(struct iwm_softc *sc) { IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); } static void iwm_disable_interrupts(struct iwm_softc *sc) { /* disable interrupts */ IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); /* acknowledge all interrupts */ IWM_WRITE(sc, IWM_CSR_INT, ~0); IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0); } static void iwm_ict_reset(struct iwm_softc *sc) { iwm_disable_interrupts(sc); /* Reset ICT table. */ memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE); sc->ict_cur = 0; /* Set physical address of ICT table (4KB aligned). */ IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, IWM_CSR_DRAM_INT_TBL_ENABLE + | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); /* Switch to ICT interrupt mode in driver. */ sc->sc_flags |= IWM_FLAG_USE_ICT; /* Re-enable interrupts. */ IWM_WRITE(sc, IWM_CSR_INT, ~0); iwm_enable_interrupts(sc); } /* iwlwifi pcie/trans.c */ /* * Since this .. hard-resets things, it's time to actually * mark the first vap (if any) as having no mac context. * It's annoying, but since the driver is potentially being * stop/start'ed whilst active (thanks openbsd port!) we * have to correctly track this. */ static void iwm_stop_device(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); int chnl, ntries; int qid; /* tell the device to stop sending interrupts */ iwm_disable_interrupts(sc); /* * FreeBSD-local: mark the first vap as not-uploaded, * so the next transition through auth/assoc * will correctly populate the MAC context. */ if (vap) { struct iwm_vap *iv = IWM_VAP(vap); iv->is_uploaded = 0; } /* device going down, Stop using ICT table */ sc->sc_flags &= ~IWM_FLAG_USE_ICT; /* stop tx and rx. tx and rx bits, as usual, are from if_iwn */ iwm_write_prph(sc, IWM_SCD_TXFACT, 0); /* Stop all DMA channels. */ if (iwm_nic_lock(sc)) { for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0); for (ntries = 0; ntries < 200; ntries++) { uint32_t r; r = IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG); if (r & IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE( chnl)) break; DELAY(20); } } iwm_nic_unlock(sc); } iwm_disable_rx_dma(sc); /* Stop RX ring. */ iwm_reset_rx_ring(sc, &sc->rxq); /* Reset all TX rings. */ for (qid = 0; qid < nitems(sc->txq); qid++) iwm_reset_tx_ring(sc, &sc->txq[qid]); /* * Power-down device's busmaster DMA clocks */ iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); DELAY(5); /* Make sure (redundant) we've released our request to stay awake */ IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* Stop the device, and put it in low power state */ iwm_apm_stop(sc); /* Upon stop, the APM issues an interrupt if HW RF kill is set. * Clean again the interrupt here */ iwm_disable_interrupts(sc); /* stop and reset the on-board processor */ - IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_NEVO_RESET); + IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); /* * Even if we stop the HW, we still want the RF kill * interrupt */ iwm_enable_rfkill_int(sc); iwm_check_rfkill(sc); } /* iwlwifi: mvm/ops.c */ static void iwm_mvm_nic_config(struct iwm_softc *sc) { uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash; uint32_t reg_val = 0; radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >> IWM_FW_PHY_CFG_RADIO_TYPE_POS; radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >> IWM_FW_PHY_CFG_RADIO_STEP_POS; radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >> IWM_FW_PHY_CFG_RADIO_DASH_POS; /* SKU control */ reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) << IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) << IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; /* radio configuration */ reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val); IWM_DPRINTF(sc, IWM_DEBUG_RESET, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, radio_cfg_step, radio_cfg_dash); /* * W/A : NIC is stuck in a reset state after Early PCIe power off * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ - iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, - IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, - ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, + IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, + ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); + } } static int iwm_nic_rx_init(struct iwm_softc *sc) { if (!iwm_nic_lock(sc)) return EBUSY; /* * Initialize RX ring. This is from the iwn driver. */ memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); /* stop DMA */ iwm_disable_rx_dma(sc); IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0); IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); /* Set physical address of RX ring (256-byte aligned). */ IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8); /* Set physical address of RX status (16-byte aligned). */ IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); /* Enable RX. */ - /* - * Note: Linux driver also sets this: - * (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | - * - * It causes weird behavior. YMMV. - */ IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF); /* W/A for interrupt coalescing bug in 7260 and 3160 */ if (sc->host_interrupt_operation_mode) IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE); /* * Thus sayeth el jefe (iwlwifi) via a comment: * * This value should initially be 0 (before preparing any - * RBs), should be 8 after preparing the first 8 RBs (for example) + * RBs), should be 8 after preparing the first 8 RBs (for example) */ IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); iwm_nic_unlock(sc); return 0; } static int iwm_nic_tx_init(struct iwm_softc *sc) { int qid; if (!iwm_nic_lock(sc)) return EBUSY; /* Deactivate TX scheduler. */ iwm_write_prph(sc, IWM_SCD_TXFACT, 0); /* Set physical address of "keep warm" page (16-byte aligned). */ IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4); /* Initialize TX rings. */ for (qid = 0; qid < nitems(sc->txq); qid++) { struct iwm_tx_ring *txq = &sc->txq[qid]; /* Set physical address of TX ring (256-byte aligned). */ IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid), txq->desc_dma.paddr >> 8); IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: loading ring %d descriptors (%p) at %lx\n", __func__, qid, txq->desc, (unsigned long) (txq->desc_dma.paddr >> 8)); } + + iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); + iwm_nic_unlock(sc); return 0; } static int iwm_nic_init(struct iwm_softc *sc) { int error; iwm_apm_init(sc); - iwm_set_pwr(sc); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_set_pwr(sc); iwm_mvm_nic_config(sc); if ((error = iwm_nic_rx_init(sc)) != 0) return error; /* * Ditto for TX, from iwn */ if ((error = iwm_nic_tx_init(sc)) != 0) return error; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "%s: shadow registers enabled\n", __func__); IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff); return 0; } const uint8_t iwm_mvm_ac_to_tx_fifo[] = { IWM_MVM_TX_FIFO_VO, IWM_MVM_TX_FIFO_VI, IWM_MVM_TX_FIFO_BE, IWM_MVM_TX_FIFO_BK, }; -static void -iwm_enable_txq(struct iwm_softc *sc, int qid, int fifo) +static int +iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) { if (!iwm_nic_lock(sc)) { device_printf(sc->sc_dev, "%s: cannot enable txq %d\n", __func__, qid); - return; /* XXX return EBUSY */ + return EBUSY; } - /* unactivate before configuration */ - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) - | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - if (qid != IWM_MVM_CMD_QUEUE) { - iwm_set_bits_prph(sc, IWM_SCD_QUEUECHAIN_SEL, (1 << qid)); - } + if (qid == IWM_MVM_CMD_QUEUE) { + /* unactivate before configuration */ + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) + | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); - iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); - IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); - iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); - /* Set scheduler window size and frame limit. */ - iwm_write_mem32(sc, - sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + - sizeof(uint32_t), - ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); + /* Set scheduler window size and frame limit. */ + iwm_write_mem32(sc, + sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + + sizeof(uint32_t), + ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | - IWM_SCD_QUEUE_STTS_REG_MSK); + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | + IWM_SCD_QUEUE_STTS_REG_MSK); + } else { + struct iwm_scd_txq_cfg_cmd cmd; + int error; + iwm_nic_unlock(sc); + + memset(&cmd, 0, sizeof(cmd)); + cmd.scd_queue = qid; + cmd.enable = 1; + cmd.sta_id = sta_id; + cmd.tx_fifo = fifo; + cmd.aggregate = 0; + cmd.window = IWM_FRAME_LIMIT; + + error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, + sizeof(cmd), &cmd); + if (error) { + device_printf(sc->sc_dev, + "cannot enable txq %d\n", qid); + return error; + } + + if (!iwm_nic_lock(sc)) + return EBUSY; + } + + iwm_write_prph(sc, IWM_SCD_EN_CTRL, + iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); + iwm_nic_unlock(sc); - IWM_DPRINTF(sc, IWM_DEBUG_XMIT, - "%s: enabled txq %d FIFO %d\n", + IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n", __func__, qid, fifo); + + return 0; } static int iwm_post_alive(struct iwm_softc *sc) { int nwords; int error, chnl; + uint32_t base; if (!iwm_nic_lock(sc)) return EBUSY; - if (sc->sched_base != iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR)) { + base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); + if (sc->sched_base != base) { device_printf(sc->sc_dev, - "%s: sched addr mismatch", - __func__); - error = EINVAL; - goto out; + "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", + __func__, sc->sched_base, base); } iwm_ict_reset(sc); /* Clear TX scheduler state in SRAM. */ nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - IWM_SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(uint32_t); error = iwm_write_mem(sc, sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, NULL, nwords); if (error) goto out; /* Set physical address of TX scheduler rings (1KB aligned). */ iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); + iwm_nic_unlock(sc); + /* enable command channel */ - iwm_enable_txq(sc, IWM_MVM_CMD_QUEUE, 7); + error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); + if (error) + return error; + if (!iwm_nic_lock(sc)) + return EBUSY; + iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); /* Enable DMA channels. */ for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); } IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ - iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, - IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { + iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, + IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + } out: - iwm_nic_unlock(sc); + iwm_nic_unlock(sc); return error; } /* * NVM read access and content parsing. We do not support * external NVM or writing NVM. * iwlwifi/mvm/nvm.c */ /* list of NVM sections we are allowed/need to read */ const int nvm_to_read[] = { IWM_NVM_SECTION_TYPE_HW, IWM_NVM_SECTION_TYPE_SW, + IWM_NVM_SECTION_TYPE_REGULATORY, IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, + IWM_NVM_SECTION_TYPE_HW_8000, + IWM_NVM_SECTION_TYPE_MAC_OVERRIDE, + IWM_NVM_SECTION_TYPE_PHY_SKU, }; /* Default NVM size to read */ -#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWM_MAX_NVM_SECTION_SIZE 7000 +#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWM_MAX_NVM_SECTION_SIZE 8192 #define IWM_NVM_WRITE_OPCODE 1 #define IWM_NVM_READ_OPCODE 0 +/* load nvm chunk response */ +#define IWM_READ_NVM_CHUNK_SUCCEED 0 +#define IWM_READ_NVM_CHUNK_INVALID_ADDRESS 1 + static int iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) { offset = 0; struct iwm_nvm_access_cmd nvm_access_cmd = { .offset = htole16(offset), .length = htole16(length), .type = htole16(section), .op_code = IWM_NVM_READ_OPCODE, }; struct iwm_nvm_access_resp *nvm_resp; struct iwm_rx_packet *pkt; struct iwm_host_cmd cmd = { .id = IWM_NVM_ACCESS_CMD, .flags = IWM_CMD_SYNC | IWM_CMD_WANT_SKB | IWM_CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; - int ret, bytes_read, offset_read; + int ret, offset_read; + size_t bytes_read; uint8_t *resp_data; cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); ret = iwm_send_cmd(sc, &cmd); - if (ret) + if (ret) { + device_printf(sc->sc_dev, + "Could not send NVM_ACCESS command (error=%d)\n", ret); return ret; + } pkt = cmd.resp_pkt; if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { device_printf(sc->sc_dev, - "%s: Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", - __func__, pkt->hdr.flags); + "Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", + pkt->hdr.flags); ret = EIO; goto exit; } /* Extract NVM response */ nvm_resp = (void *)pkt->data; ret = le16toh(nvm_resp->status); bytes_read = le16toh(nvm_resp->length); offset_read = le16toh(nvm_resp->offset); resp_data = nvm_resp->data; if (ret) { - device_printf(sc->sc_dev, - "%s: NVM access command failed with status %d\n", - __func__, ret); + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "NVM access command failed with status %d\n", ret); ret = EINVAL; goto exit; } if (offset_read != offset) { device_printf(sc->sc_dev, - "%s: NVM ACCESS response with invalid offset %d\n", - __func__, offset_read); + "NVM ACCESS response with invalid offset %d\n", + offset_read); ret = EINVAL; goto exit; } + if (bytes_read > length) { + device_printf(sc->sc_dev, + "NVM ACCESS response with too much data " + "(%d bytes requested, %zd bytes received)\n", + length, bytes_read); + ret = EINVAL; + goto exit; + } + memcpy(data + offset, resp_data, bytes_read); *len = bytes_read; exit: iwm_free_resp(sc, &cmd); return ret; } /* * Reads an NVM section completely. - * NICs prior to 7000 family doesn't have a real NVM, but just read + * NICs prior to 7000 family don't have a real NVM, but just read * section 0 which is the EEPROM. Because the EEPROM reading is unlimited * by uCode, we need to manually check in this case that we don't * overflow and try to read more than the EEPROM size. * For 7000 family NICs, we supply the maximal size we can read, and * the uCode fills the response with as much data as we can, * without overflowing, so no check is needed. */ static int iwm_nvm_read_section(struct iwm_softc *sc, - uint16_t section, uint8_t *data, uint16_t *len) + uint16_t section, uint8_t *data, uint16_t *len, size_t max_len) { - uint16_t length, seglen; - int error; + uint16_t chunklen, seglen; + int error = 0; - /* Set nvm section read length */ - length = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "reading NVM section %d\n", section); + + chunklen = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; *len = 0; - /* Read the NVM until exhausted (reading less than requested) */ - while (seglen == length) { + /* Read NVM chunks until exhausted (reading less than requested) */ + while (seglen == chunklen && *len < max_len) { error = iwm_nvm_read_chunk(sc, - section, *len, length, data, &seglen); + section, *len, chunklen, data, &seglen); if (error) { - device_printf(sc->sc_dev, - "Cannot read NVM from section " - "%d offset %d, length %d\n", - section, *len, length); + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "Cannot read from NVM section " + "%d at offset %d\n", section, *len); return error; } *len += seglen; } IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "NVM section %d read completed\n", section); - return 0; + "NVM section %d read completed (%d bytes, error=%d)\n", + section, *len, error); + return error; } /* * BEGIN IWM_NVM_PARSE */ /* iwlwifi/iwl-nvm-parse.c */ /* NVM offsets (in words) definitions */ -enum wkp_nvm_offsets { +enum iwm_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ IWM_HW_ADDR = 0x15, /* NVM SW-Section offset (in words) definitions */ IWM_NVM_SW_SECTION = 0x1C0, IWM_NVM_VERSION = 0, IWM_RADIO_CFG = 1, IWM_SKU = 2, IWM_N_HW_ADDRS = 3, IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION, /* NVM calibration section offset (in words) definitions */ IWM_NVM_CALIB_SECTION = 0x2B8, IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION }; +enum iwm_8000_nvm_offsets { + /* NVM HW-Section offset (in words) definitions */ + IWM_HW_ADDR0_WFPM_8000 = 0x12, + IWM_HW_ADDR1_WFPM_8000 = 0x16, + IWM_HW_ADDR0_PCIE_8000 = 0x8A, + IWM_HW_ADDR1_PCIE_8000 = 0x8E, + IWM_MAC_ADDRESS_OVERRIDE_8000 = 1, + + /* NVM SW-Section offset (in words) definitions */ + IWM_NVM_SW_SECTION_8000 = 0x1C0, + IWM_NVM_VERSION_8000 = 0, + IWM_RADIO_CFG_8000 = 0, + IWM_SKU_8000 = 2, + IWM_N_HW_ADDRS_8000 = 3, + + /* NVM REGULATORY -Section offset (in words) definitions */ + IWM_NVM_CHANNELS_8000 = 0, + IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7, + IWM_NVM_LAR_OFFSET_8000 = 0x507, + IWM_NVM_LAR_ENABLED_8000 = 0x7, + + /* NVM calibration section offset (in words) definitions */ + IWM_NVM_CALIB_SECTION_8000 = 0x2B8, + IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000 +}; + /* SKU Capabilities (actual values from NVM definition) */ enum nvm_sku_bits { IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), }; /* radio config bits (actual values from NVM definition) */ #define IWM_NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ #define IWM_NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ #define IWM_NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ #define IWM_NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ #define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ #define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ +#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x) (x & 0xF) +#define IWM_NVM_RF_CFG_DASH_MSK_8000(x) ((x >> 4) & 0xF) +#define IWM_NVM_RF_CFG_STEP_MSK_8000(x) ((x >> 8) & 0xF) +#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x) ((x >> 12) & 0xFFF) +#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) +#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) + #define DEFAULT_MAX_TX_POWER 16 /** * enum iwm_nvm_channel_flags - channel flags in NVM * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed * @IWM_NVM_CHANNEL_RADAR: radar detection required * XXX cannot find this (DFS) flag in iwl-nvm-parse.c * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?) * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) */ enum iwm_nvm_channel_flags { IWM_NVM_CHANNEL_VALID = (1 << 0), IWM_NVM_CHANNEL_IBSS = (1 << 1), IWM_NVM_CHANNEL_ACTIVE = (1 << 3), IWM_NVM_CHANNEL_RADAR = (1 << 4), IWM_NVM_CHANNEL_DFS = (1 << 7), IWM_NVM_CHANNEL_WIDE = (1 << 8), IWM_NVM_CHANNEL_40MHZ = (1 << 9), IWM_NVM_CHANNEL_80MHZ = (1 << 10), IWM_NVM_CHANNEL_160MHZ = (1 << 11), }; /* * Translate EEPROM flags to net80211. */ static uint32_t iwm_eeprom_channel_flags(uint16_t ch_flags) { uint32_t nflags; nflags = 0; if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0) nflags |= IEEE80211_CHAN_PASSIVE; if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0) nflags |= IEEE80211_CHAN_NOADHOC; if (ch_flags & IWM_NVM_CHANNEL_RADAR) { nflags |= IEEE80211_CHAN_DFS; /* Just in case. */ nflags |= IEEE80211_CHAN_NOADHOC; } return (nflags); } static void iwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[], - int maxchans, int *nchans, int ch_idx, int ch_num, const uint8_t bands[]) + int maxchans, int *nchans, int ch_idx, size_t ch_num, + const uint8_t bands[]) { const uint16_t * const nvm_ch_flags = sc->sc_nvm.nvm_ch_flags; uint32_t nflags; uint16_t ch_flags; uint8_t ieee; int error; for (; ch_idx < ch_num; ch_idx++) { ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); - ieee = iwm_nvm_channels[ch_idx]; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + ieee = iwm_nvm_channels[ch_idx]; + else + ieee = iwm_nvm_channels_8000[ch_idx]; if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Ch. %d Flags %x [%sGHz] - No traffic\n", ieee, ch_flags, (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? "5.2" : "2.4"); continue; } nflags = iwm_eeprom_channel_flags(ch_flags); error = ieee80211_add_channel(chans, maxchans, nchans, ieee, 0, 0, nflags, bands); if (error != 0) break; IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Ch. %d Flags %x [%sGHz] - Added\n", ieee, ch_flags, (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? "5.2" : "2.4"); } } static void iwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans, struct ieee80211_channel chans[]) { struct iwm_softc *sc = ic->ic_softc; struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t bands[IEEE80211_MODE_BYTES]; + size_t ch_num; memset(bands, 0, sizeof(bands)); /* 1-13: 11b/g channels. */ setbit(bands, IEEE80211_MODE_11B); setbit(bands, IEEE80211_MODE_11G); iwm_add_channel_band(sc, chans, maxchans, nchans, 0, IWM_NUM_2GHZ_CHANNELS - 1, bands); /* 14: 11b channel only. */ clrbit(bands, IEEE80211_MODE_11G); iwm_add_channel_band(sc, chans, maxchans, nchans, IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands); if (data->sku_cap_band_52GHz_enable) { + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + ch_num = nitems(iwm_nvm_channels); + else + ch_num = nitems(iwm_nvm_channels_8000); memset(bands, 0, sizeof(bands)); setbit(bands, IEEE80211_MODE_11A); iwm_add_channel_band(sc, chans, maxchans, nchans, - IWM_NUM_2GHZ_CHANNELS, nitems(iwm_nvm_channels), bands); + IWM_NUM_2GHZ_CHANNELS, ch_num, bands); } } +static void +iwm_set_hw_address_8000(struct iwm_softc *sc, struct iwm_nvm_data *data, + const uint16_t *mac_override, const uint16_t *nvm_hw) +{ + const uint8_t *hw_addr; + + if (mac_override) { + static const uint8_t reserved_mac[] = { + 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 + }; + + hw_addr = (const uint8_t *)(mac_override + + IWM_MAC_ADDRESS_OVERRIDE_8000); + + /* + * Store the MAC address from MAO section. + * No byte swapping is required in MAO section + */ + IEEE80211_ADDR_COPY(data->hw_addr, hw_addr); + + /* + * Force the use of the OTP MAC address in case of reserved MAC + * address in the NVM, or if address is given but invalid. + */ + if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) && + !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) && + iwm_is_valid_ether_addr(data->hw_addr) && + !IEEE80211_IS_MULTICAST(data->hw_addr)) + return; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "%s: mac address from nvm override section invalid\n", + __func__); + } + + if (nvm_hw) { + /* read the mac address from WFMP registers */ + uint32_t mac_addr0 = + htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0)); + uint32_t mac_addr1 = + htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1)); + + hw_addr = (const uint8_t *)&mac_addr0; + data->hw_addr[0] = hw_addr[3]; + data->hw_addr[1] = hw_addr[2]; + data->hw_addr[2] = hw_addr[1]; + data->hw_addr[3] = hw_addr[0]; + + hw_addr = (const uint8_t *)&mac_addr1; + data->hw_addr[4] = hw_addr[1]; + data->hw_addr[5] = hw_addr[0]; + + return; + } + + device_printf(sc->sc_dev, "%s: mac address not found\n", __func__); + memset(data->hw_addr, 0, sizeof(data->hw_addr)); +} + static int +iwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw, + const uint16_t *phy_sku) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_SKU); + + return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000)); +} + +static int +iwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_NVM_VERSION); + else + return le32_to_cpup((const uint32_t *)(nvm_sw + + IWM_NVM_VERSION_8000)); +} + +static int +iwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw, + const uint16_t *phy_sku) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_RADIO_CFG); + + return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); +} + +static int +iwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw) +{ + int n_hw_addr; + + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) + return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); + + n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000)); + + return n_hw_addr & IWM_N_HW_ADDR_MASK; +} + +static void +iwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data, + uint32_t radio_cfg) +{ + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { + data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); + data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); + data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); + data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); + return; + } + + /* set the radio configuration for family 8000 */ + data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg); + data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg); + data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg); + data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg); + data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg); + data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg); +} + +static int iwm_parse_nvm_data(struct iwm_softc *sc, - const uint16_t *nvm_hw, const uint16_t *nvm_sw, - const uint16_t *nvm_calib, uint8_t tx_chains, uint8_t rx_chains) + const uint16_t *nvm_hw, const uint16_t *nvm_sw, + const uint16_t *nvm_calib, const uint16_t *mac_override, + const uint16_t *phy_sku, const uint16_t *regulatory) { struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t hw_addr[IEEE80211_ADDR_LEN]; - uint16_t radio_cfg, sku; + uint32_t sku, radio_cfg; - data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION); + data->nvm_version = iwm_get_nvm_version(sc, nvm_sw); - radio_cfg = le16_to_cpup(nvm_sw + IWM_RADIO_CFG); - data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); - data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); - data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); - data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); + radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku); + iwm_set_radio_cfg(sc, data, radio_cfg); - sku = le16_to_cpup(nvm_sw + IWM_SKU); + sku = iwm_get_sku(sc, nvm_sw, phy_sku); data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; data->sku_cap_11n_enable = 0; - data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); + data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw); - data->xtal_calib[0] = *(nvm_calib + IWM_XTAL_CALIB); - data->xtal_calib[1] = *(nvm_calib + IWM_XTAL_CALIB + 1); - /* The byte order is little endian 16 bit, meaning 214365 */ - IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR); - data->hw_addr[0] = hw_addr[1]; - data->hw_addr[1] = hw_addr[0]; - data->hw_addr[2] = hw_addr[3]; - data->hw_addr[3] = hw_addr[2]; - data->hw_addr[4] = hw_addr[5]; - data->hw_addr[5] = hw_addr[4]; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR); + data->hw_addr[0] = hw_addr[1]; + data->hw_addr[1] = hw_addr[0]; + data->hw_addr[2] = hw_addr[3]; + data->hw_addr[3] = hw_addr[2]; + data->hw_addr[4] = hw_addr[5]; + data->hw_addr[5] = hw_addr[4]; + } else { + iwm_set_hw_address_8000(sc, data, mac_override, nvm_hw); + } - memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS], - sizeof(data->nvm_ch_flags)); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS], + IWM_NUM_CHANNELS * sizeof(uint16_t)); + } else { + memcpy(data->nvm_ch_flags, ®ulatory[IWM_NVM_CHANNELS_8000], + IWM_NUM_CHANNELS_8000 * sizeof(uint16_t)); + } data->calib_version = 255; /* TODO: this value will prevent some checks from failing, we need to check if this field is still needed, and if it does, where is it in the NVM */ return 0; } /* * END NVM PARSE */ -struct iwm_nvm_section { - uint16_t length; - uint8_t *data; -}; - static int iwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) { - const uint16_t *hw, *sw, *calib; + const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; /* Checking for required sections */ - if (!sections[IWM_NVM_SECTION_TYPE_SW].data || - !sections[IWM_NVM_SECTION_TYPE_HW].data) { - device_printf(sc->sc_dev, - "%s: Can't parse empty NVM sections\n", - __func__); - return ENOENT; + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + if (!sections[IWM_NVM_SECTION_TYPE_SW].data || + !sections[IWM_NVM_SECTION_TYPE_HW].data) { + device_printf(sc->sc_dev, + "Can't parse empty OTP/NVM sections\n"); + return ENOENT; + } + + hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data; + } else if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + /* SW and REGULATORY sections are mandatory */ + if (!sections[IWM_NVM_SECTION_TYPE_SW].data || + !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) { + device_printf(sc->sc_dev, + "Can't parse empty OTP/NVM sections\n"); + return ENOENT; + } + /* MAC_OVERRIDE or at least HW section must exist */ + if (!sections[IWM_NVM_SECTION_TYPE_HW_8000].data && + !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) { + device_printf(sc->sc_dev, + "Can't parse mac_address, empty sections\n"); + return ENOENT; + } + + /* PHY_SKU section is mandatory in B0 */ + if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) { + device_printf(sc->sc_dev, + "Can't parse phy_sku in B0, empty sections\n"); + return ENOENT; + } + + hw = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_HW_8000].data; + } else { + panic("unknown device family %d\n", sc->sc_device_family); } - hw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_HW].data; sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; - calib = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; - return iwm_parse_nvm_data(sc, hw, sw, calib, - IWM_FW_VALID_TX_ANT(sc), IWM_FW_VALID_RX_ANT(sc)); + calib = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; + regulatory = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_REGULATORY].data; + mac_override = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data; + phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data; + + return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override, + phy_sku, regulatory); } static int iwm_nvm_init(struct iwm_softc *sc) { struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS]; int i, section, error; uint16_t len; - uint8_t *nvm_buffer, *temp; + uint8_t *buf; + const size_t bufsz = IWM_MAX_NVM_SECTION_SIZE; - /* Read From FW NVM */ - IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, - "%s: Read NVM\n", - __func__); + memset(nvm_sections, 0 , sizeof(nvm_sections)); - memset(nvm_sections, 0, sizeof(nvm_sections)); + buf = malloc(bufsz, M_DEVBUF, M_NOWAIT); + if (buf == NULL) + return ENOMEM; - /* TODO: find correct NVM max size for a section */ - nvm_buffer = malloc(IWM_OTP_LOW_IMAGE_SIZE, M_DEVBUF, M_NOWAIT); - if (nvm_buffer == NULL) - return (ENOMEM); for (i = 0; i < nitems(nvm_to_read); i++) { section = nvm_to_read[i]; KASSERT(section <= nitems(nvm_sections), ("too many sections")); - error = iwm_nvm_read_section(sc, section, nvm_buffer, &len); - if (error) - break; - - temp = malloc(len, M_DEVBUF, M_NOWAIT); - if (temp == NULL) { + error = iwm_nvm_read_section(sc, section, buf, &len, bufsz); + if (error) { + error = 0; + continue; + } + nvm_sections[section].data = malloc(len, M_DEVBUF, M_NOWAIT); + if (nvm_sections[section].data == NULL) { error = ENOMEM; break; } - memcpy(temp, nvm_buffer, len); - nvm_sections[section].data = temp; + memcpy(nvm_sections[section].data, buf, len); nvm_sections[section].length = len; } - free(nvm_buffer, M_DEVBUF); + free(buf, M_DEVBUF); if (error == 0) error = iwm_parse_nvm_sections(sc, nvm_sections); for (i = 0; i < IWM_NVM_NUM_OF_SECTIONS; i++) { if (nvm_sections[i].data != NULL) free(nvm_sections[i].data, M_DEVBUF); } return error; } /* * Firmware loading gunk. This is kind of a weird hybrid between the * iwn driver and the Linux iwlwifi driver. */ static int -iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, +iwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr, const uint8_t *section, uint32_t byte_cnt) { + int error = EINVAL; + uint32_t chunk_sz, offset; + + chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt); + + for (offset = 0; offset < byte_cnt; offset += chunk_sz) { + uint32_t addr, len; + const uint8_t *data; + + addr = dst_addr + offset; + len = MIN(chunk_sz, byte_cnt - offset); + data = section + offset; + + error = iwm_firmware_load_chunk(sc, addr, data, len); + if (error) + break; + } + + return error; +} + +static int +iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, + const uint8_t *chunk, uint32_t byte_cnt) +{ struct iwm_dma_info *dma = &sc->fw_dma; int error; - /* Copy firmware section into pre-allocated DMA-safe memory. */ - memcpy(dma->vaddr, section, byte_cnt); + /* Copy firmware chunk into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, chunk, byte_cnt); bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); - if (!iwm_nic_lock(sc)) - return EBUSY; + if (dst_addr >= IWM_FW_MEM_EXTENDED_START && + dst_addr <= IWM_FW_MEM_EXTENDED_END) { + iwm_set_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); + } sc->sc_fw_chunk_done = 0; + if (!iwm_nic_lock(sc)) + return EBUSY; + IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), dst_addr); IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), (iwm_get_dma_hi_addr(dma->paddr) << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); iwm_nic_unlock(sc); /* wait 1s for this segment to load */ while (!sc->sc_fw_chunk_done) if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0) break; + if (!sc->sc_fw_chunk_done) { + device_printf(sc->sc_dev, + "fw chunk addr 0x%x len %d failed to load\n", + dst_addr, byte_cnt); + } + + if (dst_addr >= IWM_FW_MEM_EXTENDED_START && + dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) { + iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); + iwm_nic_unlock(sc); + } + return error; } +int +iwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, + int cpu, int *first_ucode_section) +{ + int shift_param; + int i, error = 0, sec_num = 0x1; + uint32_t val, last_read_idx = 0; + const void *data; + uint32_t dlen; + uint32_t offset; + + if (cpu == 1) { + shift_param = 0; + *first_ucode_section = 0; + } else { + shift_param = 16; + (*first_ucode_section)++; + } + + for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) { + last_read_idx = i; + data = fws->fw_sect[i].fws_data; + dlen = fws->fw_sect[i].fws_len; + offset = fws->fw_sect[i].fws_devoff; + + /* + * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between + * CPU1 to CPU2. + * PAGING_SEPARATOR_SECTION delimiter - separate between + * CPU2 non paged to CPU2 paging sec. + */ + if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + offset == IWM_PAGING_SEPARATOR_SECTION) + break; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n", + i, offset, dlen, cpu); + + if (dlen > sc->sc_fwdmasegsz) { + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "chunk %d too large (%d bytes)\n", i, dlen); + error = EFBIG; + } else { + error = iwm_firmware_load_sect(sc, offset, data, dlen); + } + if (error) { + device_printf(sc->sc_dev, + "could not load firmware chunk %d (error %d)\n", + i, error); + return error; + } + + /* Notify the ucode of the loaded section number and status */ + if (iwm_nic_lock(sc)) { + val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS); + val = val | (sec_num << shift_param); + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val); + sec_num = (sec_num << 1) | 0x1; + iwm_nic_unlock(sc); + + /* + * The firmware won't load correctly without this delay. + */ + DELAY(8000); + } + } + + *first_ucode_section = last_read_idx; + + if (iwm_nic_lock(sc)) { + if (cpu == 1) + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); + else + IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); + iwm_nic_unlock(sc); + } + + return 0; +} + +int +iwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +{ + struct iwm_fw_sects *fws; + int error = 0; + int first_ucode_section; + + IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n", + ucode_type); + + fws = &sc->sc_fw.fw_sects[ucode_type]; + + /* configure the ucode to be ready to get the secured image */ + /* release CPU reset */ + iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT); + + /* load to FW the binary Secured sections of CPU1 */ + error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section); + if (error) + return error; + + /* load to FW the binary sections of CPU2 */ + return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section); +} + static int -iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +iwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { struct iwm_fw_sects *fws; - int error, i, w; + int error, i; const void *data; uint32_t dlen; uint32_t offset; sc->sc_uc.uc_intr = 0; fws = &sc->sc_fw.fw_sects[ucode_type]; for (i = 0; i < fws->fw_count; i++) { data = fws->fw_sect[i].fws_data; dlen = fws->fw_sect[i].fws_len; offset = fws->fw_sect[i].fws_devoff; IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, "LOAD FIRMWARE type %d offset %u len %d\n", ucode_type, offset, dlen); - error = iwm_firmware_load_chunk(sc, offset, data, dlen); + if (dlen > sc->sc_fwdmasegsz) { + IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, + "chunk %d too large (%d bytes)\n", i, dlen); + error = EFBIG; + } else { + error = iwm_firmware_load_sect(sc, offset, data, dlen); + } if (error) { device_printf(sc->sc_dev, - "%s: chunk %u of %u returned error %02d\n", - __func__, i, fws->fw_count, error); + "could not load firmware chunk %u of %u " + "(error=%d)\n", i, fws->fw_count, error); return error; } } - /* wait for the firmware to load */ IWM_WRITE(sc, IWM_CSR_RESET, 0); + return 0; +} + +static int +iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +{ + int error, w; + + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + error = iwm_load_firmware_8000(sc, ucode_type); + else + error = iwm_load_firmware_7000(sc, ucode_type); + if (error) + return error; + + /* wait for the firmware to load */ for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10); } + if (error || !sc->sc_uc.uc_ok) { + device_printf(sc->sc_dev, "could not load firmware\n"); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + device_printf(sc->sc_dev, "cpu1 status: 0x%x\n", + iwm_read_prph(sc, IWM_SB_CPU_1_STATUS)); + device_printf(sc->sc_dev, "cpu2 status: 0x%x\n", + iwm_read_prph(sc, IWM_SB_CPU_2_STATUS)); + } + } + /* + * Give the firmware some time to initialize. + * Accessing it too early causes errors. + */ + msleep(&w, &sc->sc_mtx, 0, "iwmfwinit", hz); + return error; } /* iwlwifi: pcie/trans.c */ static int iwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { int error; IWM_WRITE(sc, IWM_CSR_INT, ~0); if ((error = iwm_nic_init(sc)) != 0) { device_printf(sc->sc_dev, "unable to init nic\n"); return error; } /* make sure rfkill handshake bits are cleared */ IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); /* clear (again), then enable host interrupts */ IWM_WRITE(sc, IWM_CSR_INT, ~0); iwm_enable_interrupts(sc); /* really make sure rfkill handshake bits are cleared */ /* maybe we should write a few times more? just to make sure */ IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ return iwm_load_firmware(sc, ucode_type); } static int iwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) { struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { .valid = htole32(valid_tx_ant), }; return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); } /* iwlwifi: mvm/fw.c */ static int iwm_send_phy_cfg_cmd(struct iwm_softc *sc) { struct iwm_phy_cfg_cmd phy_cfg_cmd; enum iwm_ucode_type ucode_type = sc->sc_uc_current; /* Set parameters */ phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); phy_cfg_cmd.calib_control.event_trigger = sc->sc_default_calib[ucode_type].event_trigger; phy_cfg_cmd.calib_control.flow_trigger = sc->sc_default_calib[ucode_type].flow_trigger; IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, sizeof(phy_cfg_cmd), &phy_cfg_cmd); } static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { enum iwm_ucode_type old_type = sc->sc_uc_current; int error; - if ((error = iwm_read_firmware(sc, ucode_type)) != 0) + if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { + device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", + error); return error; + } sc->sc_uc_current = ucode_type; error = iwm_start_fw(sc, ucode_type); if (error) { + device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error); sc->sc_uc_current = old_type; return error; } - return iwm_post_alive(sc); + error = iwm_post_alive(sc); + if (error) { + device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error); + } + return error; } /* * mvm misc bits */ /* * follows iwlwifi/fw.c */ static int iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) { int error; /* do not operate with rfkill switch turned on */ if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { device_printf(sc->sc_dev, "radio is disabled by hardware switch\n"); return EPERM; } sc->sc_init_complete = 0; if ((error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_INIT)) != 0) { device_printf(sc->sc_dev, "failed to load init firmware\n"); return error; } if (justnvm) { if ((error = iwm_nvm_init(sc)) != 0) { device_printf(sc->sc_dev, "failed to read nvm\n"); return error; } IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->sc_nvm.hw_addr); - sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd) - + sc->sc_capa_max_probe_len - + IWM_MAX_NUM_SCAN_CHANNELS - * sizeof(struct iwm_scan_channel); - sc->sc_scan_cmd = malloc(sc->sc_scan_cmd_len, M_DEVBUF, - M_NOWAIT); - if (sc->sc_scan_cmd == NULL) - return (ENOMEM); - return 0; } + if ((error = iwm_send_bt_init_conf(sc)) != 0) { + device_printf(sc->sc_dev, + "failed to send bt coex configuration: %d\n", error); + return error; + } + + /* Init Smart FIFO. */ + error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF); + if (error != 0) + return error; + /* Send TX valid antennas before triggering calibrations */ - if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) + if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { + device_printf(sc->sc_dev, + "failed to send antennas before calibration: %d\n", error); return error; + } /* - * Send phy configurations command to init uCode - * to start the 16.0 uCode init image internal calibrations. - */ + * Send phy configurations command to init uCode + * to start the 16.0 uCode init image internal calibrations. + */ if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { device_printf(sc->sc_dev, "%s: failed to run internal calibration: %d\n", __func__, error); return error; } /* * Nothing to do but wait for the init complete notification * from the firmware */ - while (!sc->sc_init_complete) - if ((error = msleep(&sc->sc_init_complete, &sc->sc_mtx, - 0, "iwminit", 2*hz)) != 0) + while (!sc->sc_init_complete) { + error = msleep(&sc->sc_init_complete, &sc->sc_mtx, + 0, "iwminit", 2*hz); + if (error) { + device_printf(sc->sc_dev, "init complete failed: %d\n", + sc->sc_init_complete); break; + } + } + IWM_DPRINTF(sc, IWM_DEBUG_RESET, "init %scomplete\n", + sc->sc_init_complete ? "" : "not "); + return error; } /* * receive side */ /* (re)stock rx ring, called at init-time and at runtime */ static int iwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) { struct iwm_rx_ring *ring = &sc->rxq; struct iwm_rx_data *data = &ring->data[idx]; struct mbuf *m; bus_dmamap_t dmamap = NULL; - int error; - bus_addr_t paddr; + bus_dma_segment_t seg; + int nsegs, error; m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); if (m == NULL) return ENOBUFS; m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; - error = bus_dmamap_load(ring->data_dmat, ring->spare_map, - mtod(m, void *), IWM_RBUF_SIZE, iwm_dma_map_addr, - &paddr, BUS_DMA_NOWAIT); - if (error != 0 && error != EFBIG) { + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m, + &seg, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { device_printf(sc->sc_dev, "%s: can't map mbuf, error %d\n", __func__, error); goto fail; } if (data->m != NULL) bus_dmamap_unload(ring->data_dmat, data->map); /* Swap ring->spare_map with data->map */ dmamap = data->map; data->map = ring->spare_map; ring->spare_map = dmamap; bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD); data->m = m; /* Update RX descriptor. */ - ring->desc[idx] = htole32(paddr >> 8); + KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned")); + ring->desc[idx] = htole32(seg.ds_addr >> 8); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); return 0; fail: - m_free(m); + m_freem(m); return error; } /* iwlwifi: mvm/rx.c */ #define IWM_RSSI_OFFSET 50 static int iwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) { int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; uint32_t agc_a, agc_b; uint32_t val; val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; /* * dBm = rssi dB - agc dB - constant. * Higher AGC (higher radio gain) means lower signal. */ rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); IWM_DPRINTF(sc, IWM_DEBUG_RECV, "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); return max_rssi_dbm; } /* iwlwifi: mvm/rx.c */ /* * iwm_mvm_get_signal_strength - use new rx PHY INFO API * values are reported by the fw as positive values - need to negate * to obtain their dBM. Account for missing antennas by replacing 0 * values by -256dBm: practically 0 power and a non-feasible 8 bit value. */ static int iwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) { int energy_a, energy_b, energy_c, max_energy; uint32_t val; val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> IWM_RX_INFO_ENERGY_ANT_A_POS; energy_a = energy_a ? -energy_a : -256; energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> IWM_RX_INFO_ENERGY_ANT_B_POS; energy_b = energy_b ? -energy_b : -256; energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> IWM_RX_INFO_ENERGY_ANT_C_POS; energy_c = energy_c ? -energy_c : -256; max_energy = MAX(energy_a, energy_b); max_energy = MAX(max_energy, energy_c); IWM_DPRINTF(sc, IWM_DEBUG_RECV, "energy In A %d B %d C %d , and max %d\n", energy_a, energy_b, energy_c, max_energy); return max_energy; } static void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_rx_data *data) { struct iwm_rx_phy_info *phy_info = (void *)pkt->data; IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n"); bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); } /* * Retrieve the average noise (in dBm) among receivers. */ static int iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *stats) { int i, total, nbant, noise; total = nbant = noise = 0; for (i = 0; i < 3; i++) { noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; if (noise) { total += noise; nbant++; } } /* There should be at least one antenna but check anyway. */ return (nbant == 0) ? -127 : (total / nbant) - 107; } /* * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler * * Handles the actual data of the Rx packet from the fw */ static void iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_rx_data *data) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct ieee80211_frame *wh; struct ieee80211_node *ni; struct ieee80211_rx_stats rxs; struct mbuf *m; struct iwm_rx_phy_info *phy_info; struct iwm_rx_mpdu_res_start *rx_res; uint32_t len; uint32_t rx_pkt_status; int rssi; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); phy_info = &sc->sc_last_phy_info; rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); len = le16toh(rx_res->byte_count); rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); m = data->m; m->m_data = pkt->data + sizeof(*rx_res); m->m_pkthdr.len = m->m_len = len; if (__predict_false(phy_info->cfg_phy_cnt > 20)) { device_printf(sc->sc_dev, "dsp size out of range [0,20]: %d\n", phy_info->cfg_phy_cnt); return; } if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { IWM_DPRINTF(sc, IWM_DEBUG_RECV, "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); return; /* drop */ } if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { rssi = iwm_mvm_get_signal_strength(sc, phy_info); } else { rssi = iwm_mvm_calc_rssi(sc, phy_info); } rssi = (0 - IWM_MIN_DBM) + rssi; /* normalize */ rssi = MIN(rssi, sc->sc_max_rssi); /* clip to max. 100% */ /* replenish ring for the buffer we're going to feed to the sharks */ if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) { device_printf(sc->sc_dev, "%s: unable to add more buffers\n", __func__); return; } ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: phy_info: channel=%d, flags=0x%08x\n", __func__, le16toh(phy_info->channel), le16toh(phy_info->phy_flags)); /* * Populate an RX state struct with the provided information. */ bzero(&rxs, sizeof(rxs)); rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; rxs.c_ieee = le16toh(phy_info->channel); if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) { rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); } else { rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ); } rxs.rssi = rssi - sc->sc_noise; rxs.nf = sc->sc_noise; if (ieee80211_radiotap_active_vap(vap)) { struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; tap->wr_flags = 0; if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; tap->wr_chan_freq = htole16(rxs.c_freq); /* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */ tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); tap->wr_dbm_antsignal = (int8_t)rssi; tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; tap->wr_tsft = phy_info->system_timestamp; switch (phy_info->rate) { /* CCK rates. */ case 10: tap->wr_rate = 2; break; case 20: tap->wr_rate = 4; break; case 55: tap->wr_rate = 11; break; case 110: tap->wr_rate = 22; break; /* OFDM rates. */ case 0xd: tap->wr_rate = 12; break; case 0xf: tap->wr_rate = 18; break; case 0x5: tap->wr_rate = 24; break; case 0x7: tap->wr_rate = 36; break; case 0x9: tap->wr_rate = 48; break; case 0xb: tap->wr_rate = 72; break; case 0x1: tap->wr_rate = 96; break; case 0x3: tap->wr_rate = 108; break; /* Unknown rate: should not happen. */ default: tap->wr_rate = 0; } } IWM_UNLOCK(sc); if (ni != NULL) { IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m); ieee80211_input_mimo(ni, m, &rxs); ieee80211_free_node(ni); } else { IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m); ieee80211_input_mimo_all(ic, m, &rxs); } IWM_LOCK(sc); } static int iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_node *in) { struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; struct ieee80211_node *ni = &in->in_ni; struct ieee80211vap *vap = ni->ni_vap; int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; int failack = tx_resp->failure_frame; KASSERT(tx_resp->frame_count == 1, ("too many frames")); /* Update rate control statistics. */ IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: status=0x%04x, seq=%d, fc=%d, btc=%d, frts=%d, ff=%d, irate=%08x, wmt=%d\n", __func__, (int) le16toh(tx_resp->status.status), (int) le16toh(tx_resp->status.sequence), tx_resp->frame_count, tx_resp->bt_kill_count, tx_resp->failure_rts, tx_resp->failure_frame, le32toh(tx_resp->initial_rate), (int) le16toh(tx_resp->wireless_media_time)); if (status != IWM_TX_STATUS_SUCCESS && status != IWM_TX_STATUS_DIRECT_DONE) { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_FAILURE, &failack, NULL); return (1); } else { ieee80211_ratectl_tx_complete(vap, ni, IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL); return (0); } } static void iwm_mvm_rx_tx_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_rx_data *data) { struct iwm_cmd_header *cmd_hdr = &pkt->hdr; int idx = cmd_hdr->idx; int qid = cmd_hdr->qid; struct iwm_tx_ring *ring = &sc->txq[qid]; struct iwm_tx_data *txd = &ring->data[idx]; struct iwm_node *in = txd->in; struct mbuf *m = txd->m; int status; KASSERT(txd->done == 0, ("txd not done")); KASSERT(txd->in != NULL, ("txd without node")); KASSERT(txd->m != NULL, ("txd without mbuf")); bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); sc->sc_tx_timer = 0; status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in); /* Unmap and free mbuf. */ bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, txd->map); IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "free txd %p, in %p\n", txd, txd->in); txd->done = 1; txd->m = NULL; txd->in = NULL; ieee80211_tx_complete(&in->in_ni, m, status); if (--ring->queued < IWM_TX_RING_LOMARK) { sc->qfullmsk &= ~(1 << ring->qid); if (sc->qfullmsk == 0) { /* * Well, we're in interrupt context, but then again * I guess net80211 does all sorts of stunts in * interrupt context, so maybe this is no biggie. */ iwm_start(sc); } } } /* * transmit side */ /* * Process a "command done" firmware notification. This is where we wakeup * processes waiting for a synchronous command completion. * from if_iwn */ static void iwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) { struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; struct iwm_tx_data *data; if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { return; /* Not a command ack. */ } data = &ring->data[pkt->hdr.idx]; /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; } wakeup(&ring->desc[pkt->hdr.idx]); } #if 0 /* * necessary only for block ack mode */ void iwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, uint16_t len) { struct iwm_agn_scd_bc_tbl *scd_bc_tbl; uint16_t w_val; scd_bc_tbl = sc->sched_dma.vaddr; len += 8; /* magic numbers came naturally from paris */ if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) len = roundup(len, 4) / 4; w_val = htole16(sta_id << 12 | len); /* Update TX scheduler. */ scd_bc_tbl[qid].tfd_offset[idx] = w_val; bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, BUS_DMASYNC_PREWRITE); /* I really wonder what this is ?!? */ if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, BUS_DMASYNC_PREWRITE); } } #endif /* * Take an 802.11 (non-n) rate, find the relevant rate * table entry. return the index into in_ridx[]. * * The caller then uses that index back into in_ridx * to figure out the rate index programmed /into/ * the firmware for this given node. */ static int iwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, uint8_t rate) { int i; uint8_t r; for (i = 0; i < nitems(in->in_ridx); i++) { r = iwm_rates[in->in_ridx[i]].rate; if (rate == r) return (i); } /* XXX Return the first */ /* XXX TODO: have it return the /lowest/ */ return (0); } /* * Fill in the rate related information for a transmit command. */ static const struct iwm_rate * iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, struct ieee80211_frame *wh, struct iwm_tx_cmd *tx) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211_node *ni = &in->in_ni; const struct iwm_rate *rinfo; int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; int ridx, rate_flags; tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; /* * XXX TODO: everything about the rate selection here is terrible! */ if (type == IEEE80211_FC0_TYPE_DATA) { int i; /* for data frames, use RS table */ (void) ieee80211_ratectl_rate(ni, NULL, 0); i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); ridx = in->in_ridx[i]; /* This is the index into the programmed table */ tx->initial_rate_index = i; tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, "%s: start with i=%d, txrate %d\n", __func__, i, iwm_rates[ridx].rate); } else { /* * For non-data, use the lowest supported rate for the given * operational mode. * * Note: there may not be any rate control information available. * This driver currently assumes if we're transmitting data * frames, use the rate control table. Grr. * * XXX TODO: use the configured rate for the traffic type! * XXX TODO: this should be per-vap, not curmode; as we later * on we'll want to handle off-channel stuff (eg TDLS). */ if (ic->ic_curmode == IEEE80211_MODE_11A) { /* * XXX this assumes the mode is either 11a or not 11a; * definitely won't work for 11n. */ ridx = IWM_RIDX_OFDM; } else { ridx = IWM_RIDX_CCK; } } rinfo = &iwm_rates[ridx]; IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n", __func__, ridx, rinfo->rate, !! (IWM_RIDX_IS_CCK(ridx)) ); /* XXX TODO: hard-coded TX antenna? */ rate_flags = 1 << IWM_RATE_MCS_ANT_POS; if (IWM_RIDX_IS_CCK(ridx)) rate_flags |= IWM_RATE_MCS_CCK_MSK; tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); return rinfo; } #define TB0_SIZE 16 static int iwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) { struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwm_node *in = IWM_NODE(ni); struct iwm_tx_ring *ring; struct iwm_tx_data *data; struct iwm_tfd *desc; struct iwm_device_cmd *cmd; struct iwm_tx_cmd *tx; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; struct mbuf *m1; const struct iwm_rate *rinfo; uint32_t flags; u_int hdrlen; bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER]; int nsegs; uint8_t tid, type; int i, totlen, error, pad; wh = mtod(m, struct ieee80211_frame *); hdrlen = ieee80211_anyhdrsize(wh); type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; tid = 0; ring = &sc->txq[ac]; desc = &ring->desc[ring->cur]; memset(desc, 0, sizeof(*desc)); data = &ring->data[ring->cur]; /* Fill out iwm_tx_cmd to send to the firmware */ cmd = &ring->cmd[ring->cur]; cmd->hdr.code = IWM_TX_CMD; cmd->hdr.flags = 0; cmd->hdr.qid = ring->qid; cmd->hdr.idx = ring->cur; tx = (void *)cmd->data; memset(tx, 0, sizeof(*tx)); rinfo = iwm_tx_fill_cmd(sc, in, wh, tx); /* Encrypt the frame if need be. */ if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { /* Retrieve key for TX && do software encryption. */ k = ieee80211_crypto_encap(ni, m); if (k == NULL) { m_freem(m); return (ENOBUFS); } /* 802.11 header may have moved. */ wh = mtod(m, struct ieee80211_frame *); } if (ieee80211_radiotap_active_vap(vap)) { struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; tap->wt_flags = 0; tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); tap->wt_rate = rinfo->rate; if (k != NULL) tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; ieee80211_radiotap_tx(vap, m); } totlen = m->m_pkthdr.len; flags = 0; if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= IWM_TX_CMD_FLG_ACK; } if (type != IEEE80211_FC0_TYPE_DATA && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; } if (IEEE80211_IS_MULTICAST(wh->i_addr1) || type != IEEE80211_FC0_TYPE_DATA) tx->sta_id = sc->sc_aux_sta.sta_id; else tx->sta_id = IWM_STATION_ID; if (type == IEEE80211_FC0_TYPE_MGT) { uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) tx->pm_frame_timeout = htole16(3); else tx->pm_frame_timeout = htole16(2); } else { tx->pm_frame_timeout = htole16(0); } if (hdrlen & 3) { /* First segment length must be a multiple of 4. */ flags |= IWM_TX_CMD_FLG_MH_PAD; pad = 4 - (hdrlen & 3); } else pad = 0; tx->driver_txop = 0; tx->next_frame_len = 0; tx->len = htole16(totlen); tx->tid_tspec = tid; tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); /* Set physical address of "scratch area". */ tx->dram_lsb_ptr = htole32(data->scratch_paddr); tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); /* Copy 802.11 header in TX command. */ memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; tx->sec_ctl = 0; tx->tx_flags |= htole32(flags); /* Trim 802.11 header. */ m_adj(m, hdrlen); error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { if (error != EFBIG) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); m_freem(m); return error; } /* Too many DMA segments, linearize mbuf. */ m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2); if (m1 == NULL) { device_printf(sc->sc_dev, "%s: could not defrag mbuf\n", __func__); m_freem(m); return (ENOBUFS); } m = m1; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", error); m_freem(m); return error; } } data->m = m; data->in = in; data->done = 0; IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "sending txd %p, in %p\n", data, data->in); KASSERT(data->in != NULL, ("node is NULL")); IWM_DPRINTF(sc, IWM_DEBUG_XMIT, - "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%d\n", + "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n", ring->qid, ring->cur, totlen, nsegs, le32toh(tx->tx_flags), le32toh(tx->rate_n_flags), - (int) tx->initial_rate_index + tx->initial_rate_index ); /* Fill TX descriptor. */ desc->num_tbs = 2 + nsegs; desc->tbs[0].lo = htole32(data->cmd_paddr); desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | (TB0_SIZE << 4); desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | ((sizeof(struct iwm_cmd_header) + sizeof(*tx) + hdrlen + pad - TB0_SIZE) << 4); /* Other DMA segments are for data payload. */ for (i = 0; i < nsegs; i++) { seg = &segs[i]; desc->tbs[i+2].lo = htole32(seg->ds_addr); desc->tbs[i+2].hi_n_len = \ htole16(iwm_get_dma_hi_addr(seg->ds_addr)) | ((seg->ds_len) << 4); } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); #if 0 iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); #endif /* Kick TX ring. */ ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); /* Mark TX ring as full if we reach a certain threshold. */ if (++ring->queued > IWM_TX_RING_HIMARK) { sc->qfullmsk |= 1 << ring->qid; } return 0; } static int iwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, const struct ieee80211_bpf_params *params) { struct ieee80211com *ic = ni->ni_ic; struct iwm_softc *sc = ic->ic_softc; int error = 0; IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "->%s begin\n", __func__); if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { m_freem(m); IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "<-%s not RUNNING\n", __func__); return (ENETDOWN); } IWM_LOCK(sc); /* XXX fix this */ if (params == NULL) { error = iwm_tx(sc, m, ni, 0); } else { error = iwm_tx(sc, m, ni, 0); } sc->sc_tx_timer = 5; IWM_UNLOCK(sc); return (error); } /* * mvm/tx.c */ #if 0 /* * Note that there are transports that buffer frames before they reach * the firmware. This means that after flush_tx_path is called, the * queue might not be empty. The race-free way to handle this is to: * 1) set the station as draining * 2) flush the Tx path * 3) wait for the transport queues to be empty */ int iwm_mvm_flush_tx_path(struct iwm_softc *sc, int tfd_msk, int sync) { struct iwm_tx_path_flush_cmd flush_cmd = { .queues_ctl = htole32(tfd_msk), .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), }; int ret; ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, sync ? IWM_CMD_SYNC : IWM_CMD_ASYNC, sizeof(flush_cmd), &flush_cmd); if (ret) device_printf(sc->sc_dev, "Flushing tx queue failed: %d\n", ret); return ret; } #endif /* * BEGIN mvm/sta.c */ -static void -iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6, - struct iwm_mvm_add_sta_cmd_v5 *cmd_v5) -{ - memset(cmd_v5, 0, sizeof(*cmd_v5)); - - cmd_v5->add_modify = cmd_v6->add_modify; - cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; - cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; - IEEE80211_ADDR_COPY(cmd_v5->addr, cmd_v6->addr); - cmd_v5->sta_id = cmd_v6->sta_id; - cmd_v5->modify_mask = cmd_v6->modify_mask; - cmd_v5->station_flags = cmd_v6->station_flags; - cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; - cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; - cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; - cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; - cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; - cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; - cmd_v5->assoc_id = cmd_v6->assoc_id; - cmd_v5->beamform_flags = cmd_v6->beamform_flags; - cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; -} - static int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, - struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status) + struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status) { - struct iwm_mvm_add_sta_cmd_v5 cmd_v5; - - if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) { - return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, - sizeof(*cmd), cmd, status); - } - - iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); - - return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5), - &cmd_v5, 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_mvm_add_sta_cmd_v6 add_sta_cmd; + struct iwm_mvm_add_sta_cmd_v7 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(IWM_DEFAULT_MACID, IWM_DEFAULT_COLOR)); if (!update) { - add_sta_cmd.tfd_queue_msk = htole32(0xf); + 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) { 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_v6 cmd; + struct iwm_mvm_add_sta_cmd_v7 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) { 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 = 3; - sc->sc_aux_sta.tfd_queue_msk = 0; + 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 */ static int iwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in) { struct iwm_time_quota_cmd cmd; int i, idx, ret, num_active_macs, quota, quota_rem; int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, }; int n_ifs[IWM_MAX_BINDINGS] = {0, }; uint16_t id; memset(&cmd, 0, sizeof(cmd)); /* currently, PHY ID == binding ID */ if (in) { id = in->in_phyctxt->id; KASSERT(id < IWM_MAX_BINDINGS, ("invalid id")); colors[id] = in->in_phyctxt->color; if (1) n_ifs[id] = 1; } /* * The FW's scheduling session consists of * IWM_MVM_MAX_QUOTA fragments. Divide these fragments * equally between all the bindings that require quota */ num_active_macs = 0; for (i = 0; i < IWM_MAX_BINDINGS; i++) { cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID); num_active_macs += n_ifs[i]; } quota = 0; quota_rem = 0; if (num_active_macs) { quota = IWM_MVM_MAX_QUOTA / num_active_macs; quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs; } for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) { if (colors[i] < 0) continue; cmd.quotas[idx].id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i])); if (n_ifs[i] <= 0) { cmd.quotas[idx].quota = htole32(0); cmd.quotas[idx].max_duration = htole32(0); } else { cmd.quotas[idx].quota = htole32(quota * n_ifs[i]); cmd.quotas[idx].max_duration = htole32(0); } idx++; } /* Give the remainder of the session to the first binding */ cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem); ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC, sizeof(cmd), &cmd); if (ret) device_printf(sc->sc_dev, "%s: Failed to send quota: %d\n", __func__, ret); return ret; } /* * END mvm/quota.c */ /* * ieee80211 routines */ /* * Change to AUTH state in 80211 state machine. Roughly matches what * Linux does in bss_info_changed(). */ static int iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) { struct ieee80211_node *ni; struct iwm_node *in; struct iwm_vap *iv = IWM_VAP(vap); uint32_t duration; int error; /* * XXX i have a feeling that the vap node is being * freed from underneath us. Grr. */ ni = ieee80211_ref_node(vap->iv_bss); in = IWM_NODE(ni); IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE, "%s: called; vap=%p, bss ni=%p\n", __func__, vap, ni); in->in_assoc = 0; + error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON); + if (error != 0) + return error; + error = iwm_allow_mcast(vap, sc); if (error) { device_printf(sc->sc_dev, "%s: failed to set multicast\n", __func__); goto out; } /* * This is where it deviates from what Linux does. * * Linux iwlwifi doesn't reset the nic each time, nor does it * call ctxt_add() here. Instead, it adds it during vap creation, - * and always does does a mac_ctx_changed(). + * and always does a mac_ctx_changed(). * * The openbsd port doesn't attempt to do that - it reset things * at odd states and does the add here. * * So, until the state handling is fixed (ie, we never reset * the NIC except for a firmware failure, which should drag * the NIC back to IDLE, re-setup and re-add all the mac/phy * contexts that are required), let's do a dirty hack here. */ if (iv->is_uploaded) { if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: failed to update MAC\n", __func__); goto out; } if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], in->in_ni.ni_chan, 1, 1)) != 0) { device_printf(sc->sc_dev, "%s: failed update phy ctxt\n", __func__); goto out; } in->in_phyctxt = &sc->sc_phyctxt[0]; if ((error = iwm_mvm_binding_update(sc, in)) != 0) { device_printf(sc->sc_dev, "%s: binding update cmd\n", __func__); goto out; } if ((error = iwm_mvm_update_sta(sc, in)) != 0) { device_printf(sc->sc_dev, "%s: failed to update sta\n", __func__); goto out; } } else { if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: failed to add MAC\n", __func__); goto out; } if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], in->in_ni.ni_chan, 1, 1)) != 0) { device_printf(sc->sc_dev, "%s: failed add phy ctxt!\n", __func__); error = ETIMEDOUT; goto out; } in->in_phyctxt = &sc->sc_phyctxt[0]; if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) { device_printf(sc->sc_dev, "%s: binding add cmd\n", __func__); goto out; } if ((error = iwm_mvm_add_sta(sc, in)) != 0) { device_printf(sc->sc_dev, "%s: failed to add sta\n", __func__); goto out; } } /* * Prevent the FW from wandering off channel during association * by "protecting" the session with a time event. */ /* XXX duration is in units of TU, not MS */ duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */); DELAY(100); error = 0; out: ieee80211_free_node(ni); return (error); } static int iwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc) { struct iwm_node *in = IWM_NODE(vap->iv_bss); int error; if ((error = iwm_mvm_update_sta(sc, in)) != 0) { device_printf(sc->sc_dev, "%s: failed to update STA\n", __func__); return error; } in->in_assoc = 1; if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { device_printf(sc->sc_dev, "%s: failed to update MAC\n", __func__); return error; } return 0; } static int iwm_release(struct iwm_softc *sc, struct iwm_node *in) { /* * Ok, so *technically* the proper set of calls for going * from RUN back to SCAN is: * * iwm_mvm_power_mac_disable(sc, in); * iwm_mvm_mac_ctxt_changed(sc, in); * iwm_mvm_rm_sta(sc, in); * iwm_mvm_update_quotas(sc, NULL); * iwm_mvm_mac_ctxt_changed(sc, in); * iwm_mvm_binding_remove_vif(sc, in); * iwm_mvm_mac_ctxt_remove(sc, in); * * However, that freezes the device not matter which permutations * and modifications are attempted. Obviously, this driver is missing * something since it works in the Linux driver, but figuring out what * is missing is a little more complicated. Now, since we're going * back to nothing anyway, we'll just do a complete device reset. * Up your's, device! */ - //iwm_mvm_flush_tx_path(sc, 0xf, 1); + /* iwm_mvm_flush_tx_path(sc, 0xf, 1); */ iwm_stop_device(sc); iwm_init_hw(sc); if (in) in->in_assoc = 0; return 0; #if 0 int error; iwm_mvm_power_mac_disable(sc, in); if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); return error; } if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { device_printf(sc->sc_dev, "sta remove fail %d\n", error); return error; } error = iwm_mvm_rm_sta(sc, in); in->in_assoc = 0; iwm_mvm_update_quotas(sc, NULL); if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); return error; } iwm_mvm_binding_remove_vif(sc, in); iwm_mvm_mac_ctxt_remove(sc, in); return error; #endif } static struct ieee80211_node * iwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { return malloc(sizeof (struct iwm_node), M_80211_NODE, M_NOWAIT | M_ZERO); } static void iwm_setrates(struct iwm_softc *sc, struct iwm_node *in) { struct ieee80211_node *ni = &in->in_ni; struct iwm_lq_cmd *lq = &in->in_lq; int nrates = ni->ni_rates.rs_nrates; int i, ridx, tab = 0; int txant = 0; if (nrates > nitems(lq->rs_table)) { device_printf(sc->sc_dev, "%s: node supports %d rates, driver handles " "only %zu\n", __func__, nrates, nitems(lq->rs_table)); return; } if (nrates == 0) { device_printf(sc->sc_dev, "%s: node supports 0 rates, odd!\n", __func__); return; } /* * XXX .. and most of iwm_node is not initialised explicitly; * it's all just 0x0 passed to the firmware. */ /* first figure out which rates we should support */ /* XXX TODO: this isn't 11n aware /at all/ */ memset(&in->in_ridx, -1, sizeof(in->in_ridx)); IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: nrates=%d\n", __func__, nrates); /* * Loop over nrates and populate in_ridx from the highest * rate to the lowest rate. Remember, in_ridx[] has * IEEE80211_RATE_MAXSIZE entries! */ for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) { int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL; /* Map 802.11 rate to HW rate index. */ for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) if (iwm_rates[ridx].rate == rate) break; if (ridx > IWM_RIDX_MAX) { device_printf(sc->sc_dev, "%s: WARNING: device rate for %d not found!\n", __func__, rate); } else { IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: rate: i: %d, rate=%d, ridx=%d\n", __func__, i, rate, ridx); in->in_ridx[i] = ridx; } } /* then construct a lq_cmd based on those */ memset(lq, 0, sizeof(*lq)); lq->sta_id = IWM_STATION_ID; + /* For HT, always enable RTS/CTS to avoid excessive retries. */ + if (ni->ni_flags & IEEE80211_NODE_HT) + lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; + /* * are these used? (we don't do SISO or MIMO) * need to set them to non-zero, though, or we get an error. */ lq->single_stream_ant_msk = 1; lq->dual_stream_ant_msk = 1; /* * Build the actual rate selection table. * The lowest bits are the rates. Additionally, * CCK needs bit 9 to be set. The rest of the bits * we add to the table select the tx antenna * Note that we add the rates in the highest rate first * (opposite of ni_rates). */ /* * XXX TODO: this should be looping over the min of nrates * and LQ_MAX_RETRY_NUM. Sigh. */ for (i = 0; i < nrates; i++) { int nextant; if (txant == 0) - txant = IWM_FW_VALID_TX_ANT(sc); + txant = iwm_fw_valid_tx_ant(sc); nextant = 1<<(ffs(txant)-1); txant &= ~nextant; /* * Map the rate id into a rate index into * our hardware table containing the * configuration to use for this rate. */ ridx = in->in_ridx[i]; tab = iwm_rates[ridx].plcp; tab |= nextant << IWM_RATE_MCS_ANT_POS; if (IWM_RIDX_IS_CCK(ridx)) tab |= IWM_RATE_MCS_CCK_MSK; IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "station rate i=%d, rate=%d, hw=%x\n", i, iwm_rates[ridx].rate, tab); lq->rs_table[i] = htole32(tab); } /* then fill the rest with the lowest possible rate */ for (i = nrates; i < nitems(lq->rs_table); i++) { KASSERT(tab != 0, ("invalid tab")); lq->rs_table[i] = htole32(tab); } } static int iwm_media_change(struct ifnet *ifp) { struct ieee80211vap *vap = ifp->if_softc; struct ieee80211com *ic = vap->iv_ic; struct iwm_softc *sc = ic->ic_softc; int error; error = ieee80211_media_change(ifp); if (error != ENETRESET) return error; IWM_LOCK(sc); if (ic->ic_nrunning > 0) { iwm_stop(sc); iwm_init(sc); } IWM_UNLOCK(sc); return error; } static int iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) { struct iwm_vap *ivp = IWM_VAP(vap); struct ieee80211com *ic = vap->iv_ic; struct iwm_softc *sc = ic->ic_softc; struct iwm_node *in; int error; IWM_DPRINTF(sc, IWM_DEBUG_STATE, "switching state %s -> %s\n", ieee80211_state_name[vap->iv_state], ieee80211_state_name[nstate]); IEEE80211_UNLOCK(ic); IWM_LOCK(sc); if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state) iwm_led_blink_stop(sc); /* disable beacon filtering if we're hopping out of RUN */ if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { iwm_mvm_disable_beacon_filter(sc); if (((in = IWM_NODE(vap->iv_bss)) != NULL)) in->in_assoc = 0; iwm_release(sc, NULL); /* * It's impossible to directly go RUN->SCAN. If we iwm_release() * above then the card will be completely reinitialized, * so the driver must do everything necessary to bring the card * from INIT to SCAN. * * Additionally, upon receiving deauth frame from AP, * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH * state. This will also fail with this driver, so bring the FSM * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. * * XXX TODO: fix this for FreeBSD! */ if (nstate == IEEE80211_S_SCAN || nstate == IEEE80211_S_AUTH || nstate == IEEE80211_S_ASSOC) { IWM_DPRINTF(sc, IWM_DEBUG_STATE, "Force transition to INIT; MGT=%d\n", arg); IWM_UNLOCK(sc); IEEE80211_LOCK(ic); - vap->iv_newstate(vap, IEEE80211_S_INIT, arg); + /* Always pass arg as -1 since we can't Tx right now. */ + /* + * XXX arg is just ignored anyway when transitioning + * to IEEE80211_S_INIT. + */ + vap->iv_newstate(vap, IEEE80211_S_INIT, -1); IWM_DPRINTF(sc, IWM_DEBUG_STATE, "Going INIT->SCAN\n"); nstate = IEEE80211_S_SCAN; IEEE80211_UNLOCK(ic); IWM_LOCK(sc); } } switch (nstate) { case IEEE80211_S_INIT: - sc->sc_scanband = 0; break; case IEEE80211_S_AUTH: if ((error = iwm_auth(vap, sc)) != 0) { device_printf(sc->sc_dev, "%s: could not move to auth state: %d\n", __func__, error); break; } break; case IEEE80211_S_ASSOC: if ((error = iwm_assoc(vap, sc)) != 0) { device_printf(sc->sc_dev, "%s: failed to associate: %d\n", __func__, error); break; } break; case IEEE80211_S_RUN: { struct iwm_host_cmd cmd = { .id = IWM_LQ_CMD, .len = { sizeof(in->in_lq), }, .flags = IWM_CMD_SYNC, }; /* Update the association state, now we have it all */ /* (eg associd comes in at this point */ error = iwm_assoc(vap, sc); if (error != 0) { device_printf(sc->sc_dev, "%s: failed to update association state: %d\n", __func__, error); break; } in = IWM_NODE(vap->iv_bss); iwm_mvm_power_mac_update_mode(sc, in); iwm_mvm_enable_beacon_filter(sc, in); iwm_mvm_update_quotas(sc, in); iwm_setrates(sc, in); cmd.data[0] = &in->in_lq; if ((error = iwm_send_cmd(sc, &cmd)) != 0) { device_printf(sc->sc_dev, "%s: IWM_LQ_CMD failed\n", __func__); } + iwm_mvm_led_enable(sc); break; } default: break; } IWM_UNLOCK(sc); IEEE80211_LOCK(ic); return (ivp->iv_newstate(vap, nstate, arg)); } void iwm_endscan_cb(void *arg, int pending) { struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; - int done; - int error; IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE, "%s: scan ended\n", __func__); - IWM_LOCK(sc); - if (sc->sc_scanband == IEEE80211_CHAN_2GHZ && - sc->sc_nvm.sku_cap_band_52GHz_enable) { - done = 0; - if ((error = iwm_mvm_scan_request(sc, - IEEE80211_CHAN_5GHZ, 0, NULL, 0)) != 0) { - device_printf(sc->sc_dev, - "could not initiate 5 GHz scan\n"); - done = 1; + ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); +} + +/* + * Aging and idle timeouts for the different possible scenarios + * in default configuration + */ +static const uint32_t +iwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { + { + htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF), + htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF), + htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_MCAST_AGING_TIMER_DEF), + htole32(IWM_SF_MCAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_BA_AGING_TIMER_DEF), + htole32(IWM_SF_BA_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_TX_RE_AGING_TIMER_DEF), + htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF) + }, +}; + +/* + * Aging and idle timeouts for the different possible scenarios + * in single BSS MAC configuration. + */ +static const uint32_t +iwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { + { + htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER), + htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_AGG_UNICAST_AGING_TIMER), + htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_MCAST_AGING_TIMER), + htole32(IWM_SF_MCAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_BA_AGING_TIMER), + htole32(IWM_SF_BA_IDLE_TIMER) + }, + { + htole32(IWM_SF_TX_RE_AGING_TIMER), + htole32(IWM_SF_TX_RE_IDLE_TIMER) + }, +}; + +static void +iwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd, + struct ieee80211_node *ni) +{ + int i, j, watermark; + + sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN); + + /* + * If we are in association flow - check antenna configuration + * capabilities of the AP station, and choose the watermark accordingly. + */ + if (ni) { + if (ni->ni_flags & IEEE80211_NODE_HT) { +#ifdef notyet + if (ni->ni_rxmcs[2] != 0) + watermark = IWM_SF_W_MARK_MIMO3; + else if (ni->ni_rxmcs[1] != 0) + watermark = IWM_SF_W_MARK_MIMO2; + else +#endif + watermark = IWM_SF_W_MARK_SISO; + } else { + watermark = IWM_SF_W_MARK_LEGACY; } + /* default watermark value for unassociated mode. */ } else { - done = 1; + watermark = IWM_SF_W_MARK_MIMO2; } + sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark); - if (done) { - IWM_UNLOCK(sc); - ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); - IWM_LOCK(sc); - sc->sc_scanband = 0; + for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) { + for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) { + sf_cmd->long_delay_timeouts[i][j] = + htole32(IWM_SF_LONG_DELAY_AGING_TIMER); + } } - IWM_UNLOCK(sc); + + if (ni) { + memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout, + sizeof(iwm_sf_full_timeout)); + } else { + memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def, + sizeof(iwm_sf_full_timeout_def)); + } } static int +iwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct iwm_sf_cfg_cmd sf_cmd = { + .state = htole32(IWM_SF_FULL_ON), + }; + int ret = 0; + + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF); + + switch (new_state) { + case IWM_SF_UNINIT: + case IWM_SF_INIT_OFF: + iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL); + break; + case IWM_SF_FULL_ON: + iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss); + break; + default: + IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE, + "Invalid state: %d. not sending Smart Fifo cmd\n", + new_state); + return EINVAL; + } + + ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC, + sizeof(sf_cmd), &sf_cmd); + return ret; +} + +static int +iwm_send_bt_init_conf(struct iwm_softc *sc) +{ + struct iwm_bt_coex_cmd bt_cmd; + + bt_cmd.mode = htole32(IWM_BT_COEX_WIFI); + bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET); + + return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd), + &bt_cmd); +} + +static int +iwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) +{ + struct iwm_mcc_update_cmd mcc_cmd; + struct iwm_host_cmd hcmd = { + .id = IWM_MCC_UPDATE_CMD, + .flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB), + .data = { &mcc_cmd }, + }; + int ret; +#ifdef IWM_DEBUG + struct iwm_rx_packet *pkt; + struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL; + struct iwm_mcc_update_resp *mcc_resp; + int n_channels; + uint16_t mcc; +#endif + int resp_v2 = isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); + + memset(&mcc_cmd, 0, sizeof(mcc_cmd)); + mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]); + if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || + isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC)) + mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT; + else + mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW; + + if (resp_v2) + hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd); + else + hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1); + + IWM_DPRINTF(sc, IWM_DEBUG_NODE, + "send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], mcc_cmd.source_id); + + ret = iwm_send_cmd(sc, &hcmd); + if (ret) + return ret; + +#ifdef IWM_DEBUG + pkt = hcmd.resp_pkt; + + /* Extract MCC response */ + if (resp_v2) { + mcc_resp = (void *)pkt->data; + mcc = mcc_resp->mcc; + n_channels = le32toh(mcc_resp->n_channels); + } else { + mcc_resp_v1 = (void *)pkt->data; + mcc = mcc_resp_v1->mcc; + n_channels = le32toh(mcc_resp_v1->n_channels); + } + + /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ + if (mcc == 0) + mcc = 0x3030; /* "00" - world */ + + IWM_DPRINTF(sc, IWM_DEBUG_NODE, + "regulatory domain '%c%c' (%d channels available)\n", + mcc >> 8, mcc & 0xff, n_channels); +#endif + iwm_free_resp(sc, &hcmd); + + return 0; +} + +static void +iwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff) +{ + struct iwm_host_cmd cmd = { + .id = IWM_REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(uint32_t), }, + .data = { &backoff, }, + }; + + if (iwm_send_cmd(sc, &cmd) != 0) { + device_printf(sc->sc_dev, + "failed to change thermal tx backoff\n"); + } +} + +static int iwm_init_hw(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; - int error, i, qid; + int error, i, ac; - if ((error = iwm_start_hw(sc)) != 0) + if ((error = iwm_start_hw(sc)) != 0) { + printf("iwm_start_hw: failed %d\n", error); return error; + } if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) { + printf("iwm_run_init_mvm_ucode: failed %d\n", error); return error; } /* * should stop and start HW since that INIT * image just loaded */ iwm_stop_device(sc); if ((error = iwm_start_hw(sc)) != 0) { device_printf(sc->sc_dev, "could not initialize hardware\n"); return error; } /* omstart, this time with the regular firmware */ error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR); if (error) { device_printf(sc->sc_dev, "could not load firmware\n"); goto error; } - if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) + if ((error = iwm_send_bt_init_conf(sc)) != 0) { + device_printf(sc->sc_dev, "bt init conf failed\n"); goto error; + } + if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { + device_printf(sc->sc_dev, "antenna config failed\n"); + goto error; + } + /* Send phy db control command and then phy db calibration*/ - if ((error = iwm_send_phy_db_data(sc)) != 0) + if ((error = iwm_send_phy_db_data(sc)) != 0) { + device_printf(sc->sc_dev, "phy_db_data failed\n"); goto error; + } - if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) + if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) { + device_printf(sc->sc_dev, "phy_cfg_cmd failed\n"); goto error; + } /* Add auxiliary station for scanning */ - if ((error = iwm_mvm_add_aux_sta(sc)) != 0) + if ((error = iwm_mvm_add_aux_sta(sc)) != 0) { + device_printf(sc->sc_dev, "add_aux_sta failed\n"); goto error; + } for (i = 0; i < IWM_NUM_PHY_CTX; i++) { /* * The channel used here isn't relevant as it's * going to be overwritten in the other flows. * For now use the first channel we have. */ if ((error = iwm_mvm_phy_ctxt_add(sc, &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0) goto error; } + /* Initialize tx backoffs to the minimum. */ + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) + iwm_mvm_tt_tx_backoff(sc, 0); + error = iwm_mvm_power_update_device(sc); if (error) goto error; - /* Mark TX rings as active. */ - for (qid = 0; qid < 4; qid++) { - iwm_enable_txq(sc, qid, qid); + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) { + if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) + goto error; } + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { + if ((error = iwm_mvm_config_umac_scan(sc)) != 0) + goto error; + } + + /* Enable Tx queues. */ + for (ac = 0; ac < WME_NUM_AC; ac++) { + error = iwm_enable_txq(sc, IWM_STATION_ID, ac, + iwm_mvm_ac_to_tx_fifo[ac]); + if (error) + goto error; + } + + if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) { + device_printf(sc->sc_dev, "failed to disable beacon filter\n"); + goto error; + } + return 0; error: iwm_stop_device(sc); return error; } /* Allow multicast from our BSSID. */ static int iwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc) { struct ieee80211_node *ni = vap->iv_bss; struct iwm_mcast_filter_cmd *cmd; size_t size; int error; size = roundup(sizeof(*cmd), 4); cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); if (cmd == NULL) return ENOMEM; cmd->filter_own = 1; cmd->port_id = 0; cmd->count = 0; cmd->pass_all = 1; IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, IWM_CMD_SYNC, size, cmd); free(cmd, M_DEVBUF); return (error); } +/* + * ifnet interfaces + */ + static void iwm_init(struct iwm_softc *sc) { int error; if (sc->sc_flags & IWM_FLAG_HW_INITED) { return; } sc->sc_generation++; sc->sc_flags &= ~IWM_FLAG_STOPPED; if ((error = iwm_init_hw(sc)) != 0) { + printf("iwm_init_hw failed %d\n", error); iwm_stop(sc); return; } /* - * Ok, firmware loaded and we are jogging + * Ok, firmware loaded and we are jogging */ sc->sc_flags |= IWM_FLAG_HW_INITED; callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); } static int iwm_transmit(struct ieee80211com *ic, struct mbuf *m) { struct iwm_softc *sc; int error; sc = ic->ic_softc; IWM_LOCK(sc); if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { IWM_UNLOCK(sc); return (ENXIO); } error = mbufq_enqueue(&sc->sc_snd, m); if (error) { IWM_UNLOCK(sc); return (error); } iwm_start(sc); IWM_UNLOCK(sc); return (0); } /* * Dequeue packets from sendq and call send. */ static void iwm_start(struct iwm_softc *sc) { struct ieee80211_node *ni; struct mbuf *m; int ac = 0; IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__); while (sc->qfullmsk == 0 && (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; if (iwm_tx(sc, m, ni, ac) != 0) { if_inc_counter(ni->ni_vap->iv_ifp, IFCOUNTER_OERRORS, 1); ieee80211_free_node(ni); continue; } sc->sc_tx_timer = 15; } IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); } static void iwm_stop(struct iwm_softc *sc) { sc->sc_flags &= ~IWM_FLAG_HW_INITED; sc->sc_flags |= IWM_FLAG_STOPPED; sc->sc_generation++; - sc->sc_scanband = 0; iwm_led_blink_stop(sc); sc->sc_tx_timer = 0; iwm_stop_device(sc); } static void iwm_watchdog(void *arg) { struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); #ifdef IWM_DEBUG iwm_nic_error(sc); #endif ieee80211_restart_all(ic); - counter_u64_add(ic->ic_oerrors, 1); + counter_u64_add(sc->sc_ic.ic_oerrors, 1); return; } } callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); } static void iwm_parent(struct ieee80211com *ic) { struct iwm_softc *sc = ic->ic_softc; int startall = 0; IWM_LOCK(sc); if (ic->ic_nrunning > 0) { if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) { iwm_init(sc); startall = 1; } } else if (sc->sc_flags & IWM_FLAG_HW_INITED) iwm_stop(sc); IWM_UNLOCK(sc); if (startall) ieee80211_start_all(ic); } /* * The interrupt side of things */ /* * error dumping routines are from iwlwifi/mvm/utils.c */ /* * Note: This structure is read from the device with IO accesses, * and the reading already does the endian conversion. As it is * read with uint32_t-sized accesses, any members with a different size * need to be ordered correctly though! */ struct iwm_error_event_table { uint32_t valid; /* (nonzero) valid, (0) log is empty */ uint32_t error_id; /* type of error */ - uint32_t pc; /* program counter */ - uint32_t blink1; /* branch link */ + uint32_t trm_hw_status0; /* TRM HW status */ + uint32_t trm_hw_status1; /* TRM HW status */ uint32_t blink2; /* branch link */ uint32_t ilink1; /* interrupt link */ uint32_t ilink2; /* interrupt link */ uint32_t data1; /* error-specific data */ uint32_t data2; /* error-specific data */ uint32_t data3; /* error-specific data */ uint32_t bcon_time; /* beacon timer */ uint32_t tsf_low; /* network timestamp function timer */ uint32_t tsf_hi; /* network timestamp function timer */ uint32_t gp1; /* GP1 timer register */ uint32_t gp2; /* GP2 timer register */ - uint32_t gp3; /* GP3 timer register */ - uint32_t ucode_ver; /* uCode version */ + uint32_t fw_rev_type; /* firmware revision type */ + uint32_t major; /* uCode version major */ + uint32_t minor; /* uCode version minor */ uint32_t hw_ver; /* HW Silicon version */ uint32_t brd_ver; /* HW board version */ uint32_t log_pc; /* log program counter */ uint32_t frame_ptr; /* frame pointer */ uint32_t stack_ptr; /* stack pointer */ uint32_t hcmd; /* last host command header */ uint32_t isr0; /* isr status register LMPM_NIC_ISR0: * rxtx_flag */ uint32_t isr1; /* isr status register LMPM_NIC_ISR1: * host_flag */ uint32_t isr2; /* isr status register LMPM_NIC_ISR2: * enc_flag */ uint32_t isr3; /* isr status register LMPM_NIC_ISR3: * time_flag */ uint32_t isr4; /* isr status register LMPM_NIC_ISR4: * wico interrupt */ - uint32_t isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ uint32_t wait_event; /* wait event() caller address */ uint32_t l2p_control; /* L2pControlField */ uint32_t l2p_duration; /* L2pDurationField */ uint32_t l2p_mhvalid; /* L2pMhValidBits */ uint32_t l2p_addr_match; /* L2pAddrMatchStat */ uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on * (LMPM_PMG_SEL) */ uint32_t u_timestamp; /* indicate when the date and time of the * compilation */ uint32_t flow_handler; /* FH read/write pointers, RX credit */ +} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; + +/* + * UMAC error struct - relevant starting from family 8000 chip. + * Note: This structure is read from the device with IO accesses, + * and the reading already does the endian conversion. As it is + * read with u32-sized accesses, any members with a different size + * need to be ordered correctly though! + */ +struct iwm_umac_error_event_table { + uint32_t valid; /* (nonzero) valid, (0) log is empty */ + uint32_t error_id; /* type of error */ + uint32_t blink1; /* branch link */ + uint32_t blink2; /* branch link */ + uint32_t ilink1; /* interrupt link */ + uint32_t ilink2; /* interrupt link */ + uint32_t data1; /* error-specific data */ + uint32_t data2; /* error-specific data */ + uint32_t data3; /* error-specific data */ + uint32_t umac_major; + uint32_t umac_minor; + uint32_t frame_pointer; /* core register 27*/ + uint32_t stack_pointer; /* core register 28 */ + uint32_t cmd_header; /* latest host cmd sent to UMAC */ + uint32_t nic_isr_pref; /* ISR status register */ } __packed; #define ERROR_START_OFFSET (1 * sizeof(uint32_t)) #define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) #ifdef IWM_DEBUG struct { const char *name; uint8_t num; } advanced_lookup[] = { { "NMI_INTERRUPT_WDG", 0x34 }, { "SYSASSERT", 0x35 }, { "UCODE_VERSION_MISMATCH", 0x37 }, { "BAD_COMMAND", 0x38 }, { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, { "FATAL_ERROR", 0x3D }, { "NMI_TRM_HW_ERR", 0x46 }, { "NMI_INTERRUPT_TRM", 0x4C }, { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, { "NMI_INTERRUPT_HOST", 0x66 }, { "NMI_INTERRUPT_ACTION_PT", 0x7C }, { "NMI_INTERRUPT_UNKNOWN", 0x84 }, { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, { "ADVANCED_SYSASSERT", 0 }, }; static const char * iwm_desc_lookup(uint32_t num) { int i; for (i = 0; i < nitems(advanced_lookup) - 1; i++) if (advanced_lookup[i].num == num) return advanced_lookup[i].name; /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ return advanced_lookup[i].name; } +static void +iwm_nic_umac_error(struct iwm_softc *sc) +{ + struct iwm_umac_error_event_table table; + uint32_t base; + + base = sc->sc_uc.uc_umac_error_event_table; + + if (base < 0x800000) { + device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", + base); + return; + } + + if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { + device_printf(sc->sc_dev, "reading errlog failed\n"); + return; + } + + if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { + device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n"); + device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", + sc->sc_flags, table.valid); + } + + device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id, + iwm_desc_lookup(table.error_id)); + device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1); + device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2); + device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n", + table.ilink1); + device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n", + table.ilink2); + device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1); + device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2); + device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3); + device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major); + device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor); + device_printf(sc->sc_dev, "0x%08X | frame pointer\n", + table.frame_pointer); + device_printf(sc->sc_dev, "0x%08X | stack pointer\n", + table.stack_pointer); + device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header); + device_printf(sc->sc_dev, "0x%08X | isr status reg\n", + table.nic_isr_pref); +} + /* * Support for dumping the error log seemed like a good idea ... * but it's mostly hex junk and the only sensible thing is the * hw/ucode revision (which we know anyway). Since it's here, * I'll just leave it in, just in case e.g. the Intel guys want to * help us decipher some "ADVANCED_SYSASSERT" later. */ static void iwm_nic_error(struct iwm_softc *sc) { struct iwm_error_event_table table; uint32_t base; device_printf(sc->sc_dev, "dumping device error log\n"); base = sc->sc_uc.uc_error_event_table; - if (base < 0x800000 || base >= 0x80C000) { + if (base < 0x800000) { device_printf(sc->sc_dev, - "Not valid error log pointer 0x%08x\n", base); + "Invalid error log pointer 0x%08x\n", base); return; } - if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t)) != 0) { + if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { device_printf(sc->sc_dev, "reading errlog failed\n"); return; } if (!table.valid) { device_printf(sc->sc_dev, "errlog not found, skipping\n"); return; } if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - device_printf(sc->sc_dev, "Start IWL Error Log Dump:\n"); + device_printf(sc->sc_dev, "Start Error Log Dump:\n"); device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", sc->sc_flags, table.valid); } device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id, - iwm_desc_lookup(table.error_id)); - device_printf(sc->sc_dev, "%08X | uPc\n", table.pc); - device_printf(sc->sc_dev, "%08X | branchlink1\n", table.blink1); + iwm_desc_lookup(table.error_id)); + device_printf(sc->sc_dev, "%08X | trm_hw_status0\n", + table.trm_hw_status0); + device_printf(sc->sc_dev, "%08X | trm_hw_status1\n", + table.trm_hw_status1); device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2); device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1); device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2); device_printf(sc->sc_dev, "%08X | data1\n", table.data1); device_printf(sc->sc_dev, "%08X | data2\n", table.data2); device_printf(sc->sc_dev, "%08X | data3\n", table.data3); device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time); device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low); device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi); device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1); device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2); - device_printf(sc->sc_dev, "%08X | time gp3\n", table.gp3); - device_printf(sc->sc_dev, "%08X | uCode version\n", table.ucode_ver); + device_printf(sc->sc_dev, "%08X | uCode revision type\n", + table.fw_rev_type); + device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major); + device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor); device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver); device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver); device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd); device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0); device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1); device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2); device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3); device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4); - device_printf(sc->sc_dev, "%08X | isr_pref\n", table.isr_pref); + device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id); device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event); device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control); device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration); device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid); device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match); device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); + + if (sc->sc_uc.uc_umac_error_event_table) + iwm_nic_umac_error(sc); } #endif #define SYNC_RESP_STRUCT(_var_, _pkt_) \ do { \ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ _var_ = (void *)((_pkt_)+1); \ } while (/*CONSTCOND*/0) #define SYNC_RESP_PTR(_ptr_, _len_, _pkt_) \ do { \ bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ _ptr_ = (void *)((_pkt_)+1); \ } while (/*CONSTCOND*/0) #define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); /* * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt. * Basic structure from if_iwn */ static void iwm_notif_intr(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; uint16_t hw; bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, BUS_DMASYNC_POSTREAD); hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; + + /* + * Process responses + */ while (sc->rxq.cur != hw) { struct iwm_rx_ring *ring = &sc->rxq; struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwm_rx_packet *pkt; struct iwm_cmd_response *cresp; - int qid, idx; + int qid, idx, code; bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); pkt = mtod(data->m, struct iwm_rx_packet *); qid = pkt->hdr.qid & ~0x80; idx = pkt->hdr.idx; + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); IWM_DPRINTF(sc, IWM_DEBUG_INTR, - "rx packet qid=%d idx=%d flags=%x type=%x %d %d\n", - pkt->hdr.qid & ~0x80, pkt->hdr.idx, pkt->hdr.flags, - pkt->hdr.code, sc->rxq.cur, hw); + "rx packet qid=%d idx=%d type=%x %d %d\n", + pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, sc->rxq.cur, hw); /* * randomly get these from the firmware, no idea why. * they at least seem harmless, so just ignore them for now */ if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) || pkt->len_n_flags == htole32(0x55550000))) { ADVANCE_RXQ(sc); continue; } - switch (pkt->hdr.code) { + switch (code) { case IWM_REPLY_RX_PHY_CMD: iwm_mvm_rx_rx_phy_cmd(sc, pkt, data); break; case IWM_REPLY_RX_MPDU_CMD: iwm_mvm_rx_rx_mpdu(sc, pkt, data); break; case IWM_TX_CMD: iwm_mvm_rx_tx_cmd(sc, pkt, data); break; case IWM_MISSED_BEACONS_NOTIFICATION: { struct iwm_missed_beacons_notif *resp; int missed; /* XXX look at mac_id to determine interface ID */ struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); SYNC_RESP_STRUCT(resp, pkt); missed = le32toh(resp->consec_missed_beacons); IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE, "%s: MISSED_BEACON: mac_id=%d, " "consec_since_last_rx=%d, consec=%d, num_expect=%d " "num_rx=%d\n", __func__, le32toh(resp->mac_id), le32toh(resp->consec_missed_beacons_since_last_rx), le32toh(resp->consec_missed_beacons), le32toh(resp->num_expected_beacons), le32toh(resp->num_recvd_beacons)); /* Be paranoid */ if (vap == NULL) break; /* XXX no net80211 locking? */ if (vap->iv_state == IEEE80211_S_RUN && (ic->ic_flags & IEEE80211_F_SCAN) == 0) { if (missed > vap->iv_bmissthreshold) { /* XXX bad locking; turn into task */ IWM_UNLOCK(sc); ieee80211_beacon_miss(ic); IWM_LOCK(sc); } } break; } + case IWM_MFUART_LOAD_NOTIFICATION: + break; + case IWM_MVM_ALIVE: { - struct iwm_mvm_alive_resp *resp; - SYNC_RESP_STRUCT(resp, pkt); + struct iwm_mvm_alive_resp_v1 *resp1; + struct iwm_mvm_alive_resp_v2 *resp2; + struct iwm_mvm_alive_resp_v3 *resp3; - sc->sc_uc.uc_error_event_table - = le32toh(resp->error_event_table_ptr); - sc->sc_uc.uc_log_event_table - = le32toh(resp->log_event_table_ptr); - sc->sched_base = le32toh(resp->scd_base_ptr); - sc->sc_uc.uc_ok = resp->status == IWM_ALIVE_STATUS_OK; + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { + SYNC_RESP_STRUCT(resp1, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp1->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp1->log_event_table_ptr); + sc->sched_base = le32toh(resp1->scd_base_ptr); + if (resp1->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { + SYNC_RESP_STRUCT(resp2, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp2->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp2->log_event_table_ptr); + sc->sched_base = le32toh(resp2->scd_base_ptr); + sc->sc_uc.uc_umac_error_event_table + = le32toh(resp2->error_info_addr); + if (resp2->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } + + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { + SYNC_RESP_STRUCT(resp3, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp3->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp3->log_event_table_ptr); + sc->sched_base = le32toh(resp3->scd_base_ptr); + sc->sc_uc.uc_umac_error_event_table + = le32toh(resp3->error_info_addr); + if (resp3->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } + sc->sc_uc.uc_intr = 1; wakeup(&sc->sc_uc); break; } case IWM_CALIB_RES_NOTIF_PHY_DB: { struct iwm_calib_res_notif_phy_db *phy_db_notif; SYNC_RESP_STRUCT(phy_db_notif, pkt); iwm_phy_db_set_section(sc, phy_db_notif); break; } case IWM_STATISTICS_NOTIFICATION: { struct iwm_notif_statistics *stats; SYNC_RESP_STRUCT(stats, pkt); memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); sc->sc_noise = iwm_get_noise(&stats->rx.general); break; } case IWM_NVM_ACCESS_CMD: + case IWM_MCC_UPDATE_CMD: if (sc->sc_wantresp == ((qid << 16) | idx)) { bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); memcpy(sc->sc_cmd_resp, pkt, sizeof(sc->sc_cmd_resp)); } break; + case IWM_MCC_CHUB_UPDATE_CMD: { + struct iwm_mcc_chub_notif *notif; + SYNC_RESP_STRUCT(notif, pkt); + + sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; + sc->sc_fw_mcc[1] = notif->mcc & 0xff; + sc->sc_fw_mcc[2] = '\0'; + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "fw source %d sent CC '%s'\n", + notif->source_id, sc->sc_fw_mcc); + break; } + + case IWM_DTS_MEASUREMENT_NOTIFICATION: + break; + case IWM_PHY_CONFIGURATION_CMD: case IWM_TX_ANT_CONFIGURATION_CMD: case IWM_ADD_STA: case IWM_MAC_CONTEXT_CMD: case IWM_REPLY_SF_CFG_CMD: case IWM_POWER_TABLE_CMD: case IWM_PHY_CONTEXT_CMD: case IWM_BINDING_CONTEXT_CMD: case IWM_TIME_EVENT_CMD: case IWM_SCAN_REQUEST_CMD: + case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): + case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): + case IWM_SCAN_OFFLOAD_REQUEST_CMD: case IWM_REPLY_BEACON_FILTERING_CMD: case IWM_MAC_PM_POWER_TABLE: case IWM_TIME_QUOTA_CMD: case IWM_REMOVE_STA: case IWM_TXPATH_FLUSH: case IWM_LQ_CMD: + case IWM_BT_CONFIG: + case IWM_REPLY_THERMAL_MNG_BACKOFF: SYNC_RESP_STRUCT(cresp, pkt); if (sc->sc_wantresp == ((qid << 16) | idx)) { memcpy(sc->sc_cmd_resp, pkt, sizeof(*pkt)+sizeof(*cresp)); } break; /* ignore */ case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */ break; case IWM_INIT_COMPLETE_NOTIF: sc->sc_init_complete = 1; wakeup(&sc->sc_init_complete); break; - case IWM_SCAN_COMPLETE_NOTIFICATION: { - struct iwm_scan_complete_notif *notif; + case IWM_SCAN_OFFLOAD_COMPLETE: { + struct iwm_periodic_scan_complete *notif; SYNC_RESP_STRUCT(notif, pkt); - ieee80211_runtask(ic, &sc->sc_es_task); - break; } + break; + } + case IWM_SCAN_ITERATION_COMPLETE: { + struct iwm_lmac_scan_complete_notif *notif; + SYNC_RESP_STRUCT(notif, pkt); + ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); + break; + } + + case IWM_SCAN_COMPLETE_UMAC: { + struct iwm_umac_scan_complete *notif; + SYNC_RESP_STRUCT(notif, pkt); + + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, + "UMAC scan complete, status=0x%x\n", + notif->status); +#if 0 /* XXX This would be a duplicate scan end call */ + taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task); +#endif + break; + } + + case IWM_SCAN_ITERATION_COMPLETE_UMAC: { + struct iwm_umac_scan_iter_complete_notif *notif; + SYNC_RESP_STRUCT(notif, pkt); + + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration " + "complete, status=0x%x, %d channels scanned\n", + notif->status, notif->scanned_channels); + ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); + break; + } + case IWM_REPLY_ERROR: { struct iwm_error_resp *resp; SYNC_RESP_STRUCT(resp, pkt); device_printf(sc->sc_dev, "firmware error 0x%x, cmd 0x%x\n", le32toh(resp->error_type), resp->cmd_id); - break; } + break; + } case IWM_TIME_EVENT_NOTIFICATION: { struct iwm_time_event_notif *notif; SYNC_RESP_STRUCT(notif, pkt); IWM_DPRINTF(sc, IWM_DEBUG_INTR, "TE notif status = 0x%x action = 0x%x\n", - notif->status, notif->action); - break; } + notif->status, notif->action); + break; + } case IWM_MCAST_FILTER_CMD: break; + case IWM_SCD_QUEUE_CFG: { + struct iwm_scd_txq_cfg_rsp *rsp; + SYNC_RESP_STRUCT(rsp, pkt); + + IWM_DPRINTF(sc, IWM_DEBUG_CMD, + "queue cfg token=0x%x sta_id=%d " + "tid=%d scd_queue=%d\n", + rsp->token, rsp->sta_id, rsp->tid, + rsp->scd_queue); + break; + } + default: device_printf(sc->sc_dev, "frame %d/%d %x UNHANDLED (this should " "not happen)\n", qid, idx, pkt->len_n_flags); break; } /* * Why test bit 0x80? The Linux driver: * * There is one exception: uCode sets bit 15 when it * originates the response/notification, i.e. when the * response/notification is not a direct response to a * command sent by the driver. For example, uCode issues * IWM_REPLY_RX when it sends a received frame to the driver; * it is not a direct response to any driver command. * * Ok, so since when is 7 == 15? Well, the Linux driver * uses a slightly different format for pkt->hdr, and "qid" * is actually the upper byte of a two-byte field. */ if (!(pkt->hdr.qid & (1 << 7))) { iwm_cmd_done(sc, pkt); } ADVANCE_RXQ(sc); } IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); /* * Tell the firmware what we have processed. * Seems like the hardware gets upset unless we align * the write by 8?? */ hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); } static void iwm_intr(void *arg) { struct iwm_softc *sc = arg; int handled = 0; int r1, r2, rv = 0; int isperiodic = 0; IWM_LOCK(sc); IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); if (sc->sc_flags & IWM_FLAG_USE_ICT) { uint32_t *ict = sc->ict_dma.vaddr; int tmp; tmp = htole32(ict[sc->ict_cur]); if (!tmp) goto out_ena; /* * ok, there was something. keep plowing until we have all. */ r1 = r2 = 0; while (tmp) { r1 |= tmp; ict[sc->ict_cur] = 0; sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT; tmp = htole32(ict[sc->ict_cur]); } /* this is where the fun begins. don't ask */ if (r1 == 0xffffffff) r1 = 0; /* i am not expected to understand this */ if (r1 & 0xc0000) r1 |= 0x8000; r1 = (0xff & r1) | ((0xff00 & r1) << 16); } else { r1 = IWM_READ(sc, IWM_CSR_INT); /* "hardware gone" (where, fishing?) */ if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) goto out; r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); } if (r1 == 0 && r2 == 0) { goto out_ena; } IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); /* ignored */ handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); if (r1 & IWM_CSR_INT_BIT_SW_ERR) { int i; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); #ifdef IWM_DEBUG iwm_nic_error(sc); #endif /* Dump driver status (TX and RX rings) while we're here. */ device_printf(sc->sc_dev, "driver status:\n"); for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) { struct iwm_tx_ring *ring = &sc->txq[i]; device_printf(sc->sc_dev, " tx ring %2d: qid=%-2d cur=%-3d " "queued=%-3d\n", i, ring->qid, ring->cur, ring->queued); } device_printf(sc->sc_dev, " rx ring: cur=%d\n", sc->rxq.cur); device_printf(sc->sc_dev, " 802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state); /* Don't stop the device; just do a VAP restart */ IWM_UNLOCK(sc); if (vap == NULL) { printf("%s: null vap\n", __func__); return; } device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " "restarting\n", __func__, vap->iv_state); /* XXX TODO: turn this into a callout/taskqueue */ ieee80211_restart_all(ic); return; } if (r1 & IWM_CSR_INT_BIT_HW_ERR) { handled |= IWM_CSR_INT_BIT_HW_ERR; device_printf(sc->sc_dev, "hardware error, stopping device\n"); iwm_stop(sc); rv = 1; goto out; } /* firmware chunk loaded */ if (r1 & IWM_CSR_INT_BIT_FH_TX) { IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); handled |= IWM_CSR_INT_BIT_FH_TX; sc->sc_fw_chunk_done = 1; wakeup(&sc->sc_fw); } if (r1 & IWM_CSR_INT_BIT_RF_KILL) { handled |= IWM_CSR_INT_BIT_RF_KILL; if (iwm_check_rfkill(sc)) { device_printf(sc->sc_dev, "%s: rfkill switch, disabling interface\n", __func__); iwm_stop(sc); } } /* * The Linux driver uses periodic interrupts to avoid races. * We cargo-cult like it's going out of fashion. */ if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { handled |= IWM_CSR_INT_BIT_RX_PERIODIC; IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); isperiodic = 1; } if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) { handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX); IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); iwm_notif_intr(sc); /* enable periodic interrupt, see above */ if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic) IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_ENA); } if (__predict_false(r1 & ~handled)) IWM_DPRINTF(sc, IWM_DEBUG_INTR, "%s: unhandled interrupts: %x\n", __func__, r1); rv = 1; out_ena: iwm_restore_interrupts(sc); out: IWM_UNLOCK(sc); return; } /* * Autoconf glue-sniffing */ #define PCI_VENDOR_INTEL 0x8086 #define PCI_PRODUCT_INTEL_WL_3160_1 0x08b3 #define PCI_PRODUCT_INTEL_WL_3160_2 0x08b4 +#define PCI_PRODUCT_INTEL_WL_3165_1 0x3165 +#define PCI_PRODUCT_INTEL_WL_3165_2 0x3166 #define PCI_PRODUCT_INTEL_WL_7260_1 0x08b1 #define PCI_PRODUCT_INTEL_WL_7260_2 0x08b2 #define PCI_PRODUCT_INTEL_WL_7265_1 0x095a #define PCI_PRODUCT_INTEL_WL_7265_2 0x095b +#define PCI_PRODUCT_INTEL_WL_8260_1 0x24f3 +#define PCI_PRODUCT_INTEL_WL_8260_2 0x24f4 static const struct iwm_devices { uint16_t device; const char *name; } iwm_devices[] = { { PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" }, { PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" }, + { PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" }, + { PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" }, { PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" }, { PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" }, { PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" }, { PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" }, + { PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" }, + { PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" }, }; static int iwm_probe(device_t dev) { int i; - for (i = 0; i < nitems(iwm_devices); i++) + for (i = 0; i < nitems(iwm_devices); i++) { if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && pci_get_device(dev) == iwm_devices[i].device) { device_set_desc(dev, iwm_devices[i].name); return (BUS_PROBE_DEFAULT); } + } return (ENXIO); } static int iwm_dev_check(device_t dev) { struct iwm_softc *sc; sc = device_get_softc(dev); + sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); switch (pci_get_device(dev)) { case PCI_PRODUCT_INTEL_WL_3160_1: case PCI_PRODUCT_INTEL_WL_3160_2: sc->sc_fwname = "iwm3160fw"; sc->host_interrupt_operation_mode = 1; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; return (0); + case PCI_PRODUCT_INTEL_WL_3165_1: + case PCI_PRODUCT_INTEL_WL_3165_2: + sc->sc_fwname = "iwm7265fw"; + sc->host_interrupt_operation_mode = 0; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; + return (0); case PCI_PRODUCT_INTEL_WL_7260_1: case PCI_PRODUCT_INTEL_WL_7260_2: sc->sc_fwname = "iwm7260fw"; sc->host_interrupt_operation_mode = 1; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; return (0); case PCI_PRODUCT_INTEL_WL_7265_1: case PCI_PRODUCT_INTEL_WL_7265_2: sc->sc_fwname = "iwm7265fw"; sc->host_interrupt_operation_mode = 0; + sc->sc_device_family = IWM_DEVICE_FAMILY_7000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; return (0); + case PCI_PRODUCT_INTEL_WL_8260_1: + case PCI_PRODUCT_INTEL_WL_8260_2: + sc->sc_fwname = "iwm8000Cfw"; + sc->host_interrupt_operation_mode = 0; + sc->sc_device_family = IWM_DEVICE_FAMILY_8000; + sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; + return (0); default: device_printf(dev, "unknown adapter type\n"); return ENXIO; } } static int iwm_pci_attach(device_t dev) { struct iwm_softc *sc; int count, error, rid; uint16_t reg; sc = device_get_softc(dev); /* Clear device-specific "PCI retry timeout" register (41h). */ reg = pci_read_config(dev, 0x40, sizeof(reg)); pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); /* Enable bus-mastering and hardware bug workaround. */ pci_enable_busmaster(dev); reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg)); /* if !MSI */ if (reg & PCIM_STATUS_INTxSTATE) { reg &= ~PCIM_STATUS_INTxSTATE; } pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg)); rid = PCIR_BAR(0); sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem == NULL) { device_printf(sc->sc_dev, "can't map mem space\n"); return (ENXIO); } sc->sc_st = rman_get_bustag(sc->sc_mem); sc->sc_sh = rman_get_bushandle(sc->sc_mem); /* Install interrupt handler. */ count = 1; rid = 0; if (pci_alloc_msi(dev, &count) == 0) rid = 1; sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | (rid != 0 ? 0 : RF_SHAREABLE)); if (sc->sc_irq == NULL) { device_printf(dev, "can't map interrupt\n"); return (ENXIO); } error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, NULL, iwm_intr, sc, &sc->sc_ih); if (sc->sc_ih == NULL) { device_printf(dev, "can't establish interrupt"); return (ENXIO); } sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); return (0); } static void iwm_pci_detach(device_t dev) { struct iwm_softc *sc = device_get_softc(dev); if (sc->sc_irq != NULL) { bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->sc_irq), sc->sc_irq); pci_release_msi(dev); } if (sc->sc_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->sc_mem), sc->sc_mem); } static int iwm_attach(device_t dev) { struct iwm_softc *sc = device_get_softc(dev); struct ieee80211com *ic = &sc->sc_ic; int error; int txq_i, i; sc->sc_dev = dev; IWM_LOCK_INIT(sc); mbufq_init(&sc->sc_snd, ifqmaxlen); callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0); TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc); /* PCI attach */ error = iwm_pci_attach(dev); if (error != 0) goto fail; sc->sc_wantresp = -1; /* Check device type */ error = iwm_dev_check(dev); if (error != 0) goto fail; - sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; - /* * We now start fiddling with the hardware */ - sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); + /* + * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have + * changed, and now the revision step also includes bit 0-1 (no more + * "dash" value). To keep hw_rev backwards compatible - we'll store it + * in the old format. + */ + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | + (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); + if (iwm_prepare_card_hw(sc) != 0) { device_printf(dev, "could not initialize hardware\n"); goto fail; } + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { + int ret; + uint32_t hw_step; + + /* + * In order to recognize C step the driver should read the + * chip version id located at the AUX bus MISC address. + */ + IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, + IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + DELAY(2); + + ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, + IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (ret < 0) { + device_printf(sc->sc_dev, + "Failed to wake up the nic\n"); + goto fail; + } + + if (iwm_nic_lock(sc)) { + hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); + hw_step |= IWM_ENABLE_WFPM; + iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); + hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); + hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; + if (hw_step == 0x3) + sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | + (IWM_SILICON_C_STEP << 2); + iwm_nic_unlock(sc); + } else { + device_printf(sc->sc_dev, "Failed to lock the nic\n"); + goto fail; + } + } + /* Allocate DMA memory for firmware transfers. */ if ((error = iwm_alloc_fwmem(sc)) != 0) { device_printf(dev, "could not allocate memory for firmware\n"); goto fail; } /* Allocate "Keep Warm" page. */ if ((error = iwm_alloc_kw(sc)) != 0) { device_printf(dev, "could not allocate keep warm page\n"); goto fail; } /* We use ICT interrupts */ if ((error = iwm_alloc_ict(sc)) != 0) { device_printf(dev, "could not allocate ICT table\n"); goto fail; } /* Allocate TX scheduler "rings". */ if ((error = iwm_alloc_sched(sc)) != 0) { device_printf(dev, "could not allocate TX scheduler rings\n"); goto fail; } /* Allocate TX rings */ for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) { if ((error = iwm_alloc_tx_ring(sc, &sc->txq[txq_i], txq_i)) != 0) { device_printf(dev, "could not allocate TX ring %d\n", txq_i); goto fail; } } /* Allocate RX ring. */ if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) { device_printf(dev, "could not allocate RX ring\n"); goto fail; } /* Clear pending interrupts. */ IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); ic->ic_softc = sc; ic->ic_name = device_get_nameunit(sc->sc_dev); ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ /* Set device capabilities. */ ic->ic_caps = IEEE80211_C_STA | IEEE80211_C_WPA | /* WPA/RSN */ IEEE80211_C_WME | IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_SHPREAMBLE /* short preamble supported */ // IEEE80211_C_BGSCAN /* capable of bg scanning */ ; for (i = 0; i < nitems(sc->sc_phyctxt); i++) { sc->sc_phyctxt[i].id = i; sc->sc_phyctxt[i].color = 0; sc->sc_phyctxt[i].ref = 0; sc->sc_phyctxt[i].channel = NULL; } /* Max RSSI */ sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; sc->sc_preinit_hook.ich_func = iwm_preinit; sc->sc_preinit_hook.ich_arg = sc; if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { device_printf(dev, "config_intrhook_establish failed\n"); goto fail; } #ifdef IWM_DEBUG SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); #endif IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "<-%s\n", __func__); return 0; /* Free allocated memory if something failed during attachment. */ fail: iwm_detach_local(sc, 0); return ENXIO; } static int +iwm_is_valid_ether_addr(uint8_t *addr) +{ + char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; + + if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr)) + return (FALSE); + + return (TRUE); +} + +static int iwm_update_edca(struct ieee80211com *ic) { struct iwm_softc *sc = ic->ic_softc; device_printf(sc->sc_dev, "%s: called\n", __func__); return (0); } static void iwm_preinit(void *arg) { struct iwm_softc *sc = arg; device_t dev = sc->sc_dev; struct ieee80211com *ic = &sc->sc_ic; int error; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "->%s\n", __func__); IWM_LOCK(sc); if ((error = iwm_start_hw(sc)) != 0) { device_printf(dev, "could not initialize hardware\n"); IWM_UNLOCK(sc); goto fail; } error = iwm_run_init_mvm_ucode(sc, 1); iwm_stop_device(sc); if (error) { IWM_UNLOCK(sc); goto fail; } device_printf(dev, - "revision: 0x%x, firmware %d.%d (API ver. %d)\n", + "hw rev 0x%x, fw ver %s, address %s\n", sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, - IWM_UCODE_MAJOR(sc->sc_fwver), - IWM_UCODE_MINOR(sc->sc_fwver), - IWM_UCODE_API(sc->sc_fwver)); + sc->sc_fwver, ether_sprintf(sc->sc_nvm.hw_addr)); /* not all hardware can do 5GHz band */ if (!sc->sc_nvm.sku_cap_band_52GHz_enable) memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A])); IWM_UNLOCK(sc); iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, ic->ic_channels); /* * At this point we've committed - if we fail to do setup, * we now also have to tear down the net80211 state. */ ieee80211_ifattach(ic); ic->ic_vap_create = iwm_vap_create; ic->ic_vap_delete = iwm_vap_delete; ic->ic_raw_xmit = iwm_raw_xmit; ic->ic_node_alloc = iwm_node_alloc; ic->ic_scan_start = iwm_scan_start; ic->ic_scan_end = iwm_scan_end; ic->ic_update_mcast = iwm_update_mcast; ic->ic_getradiocaps = iwm_init_channel_map; ic->ic_set_channel = iwm_set_channel; ic->ic_scan_curchan = iwm_scan_curchan; ic->ic_scan_mindwell = iwm_scan_mindwell; ic->ic_wme.wme_update = iwm_update_edca; ic->ic_parent = iwm_parent; ic->ic_transmit = iwm_transmit; iwm_radiotap_attach(sc); if (bootverbose) ieee80211_announce(ic); IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "<-%s\n", __func__); config_intrhook_disestablish(&sc->sc_preinit_hook); return; fail: config_intrhook_disestablish(&sc->sc_preinit_hook); iwm_detach_local(sc, 0); } /* * Attach the interface to 802.11 radiotap. */ static void iwm_radiotap_attach(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "->%s begin\n", __func__); ieee80211_radiotap_attach(ic, &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), IWM_TX_RADIOTAP_PRESENT, &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), IWM_RX_RADIOTAP_PRESENT); IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "->%s end\n", __func__); } static struct ieee80211vap * iwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, enum ieee80211_opmode opmode, int flags, const uint8_t bssid[IEEE80211_ADDR_LEN], const uint8_t mac[IEEE80211_ADDR_LEN]) { struct iwm_vap *ivp; struct ieee80211vap *vap; if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ return NULL; ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO); vap = &ivp->iv_vap; ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); vap->iv_bmissthreshold = 10; /* override default */ /* Override with driver methods. */ ivp->iv_newstate = vap->iv_newstate; vap->iv_newstate = iwm_newstate; ieee80211_ratectl_init(vap); /* Complete setup. */ ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status, mac); ic->ic_opmode = opmode; return vap; } static void iwm_vap_delete(struct ieee80211vap *vap) { struct iwm_vap *ivp = IWM_VAP(vap); ieee80211_ratectl_deinit(vap); ieee80211_vap_detach(vap); free(ivp, M_80211_VAP); } static void iwm_scan_start(struct ieee80211com *ic) { struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - struct iwm_softc *sc = ic->ic_softc; + struct iwm_softc *sc = ic->ic_softc; int error; - if (sc->sc_scanband) - return; IWM_LOCK(sc); - error = iwm_mvm_scan_request(sc, IEEE80211_CHAN_2GHZ, 0, NULL, 0); - if (error) { + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) + error = iwm_mvm_umac_scan(sc); + else + error = iwm_mvm_lmac_scan(sc); + if (error != 0) { device_printf(sc->sc_dev, "could not initiate 2 GHz scan\n"); IWM_UNLOCK(sc); ieee80211_cancel_scan(vap); - sc->sc_scanband = 0; } else { iwm_led_blink_start(sc); IWM_UNLOCK(sc); } } static void iwm_scan_end(struct ieee80211com *ic) { struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); struct iwm_softc *sc = ic->ic_softc; IWM_LOCK(sc); iwm_led_blink_stop(sc); if (vap->iv_state == IEEE80211_S_RUN) iwm_mvm_led_enable(sc); IWM_UNLOCK(sc); } static void iwm_update_mcast(struct ieee80211com *ic) { } static void iwm_set_channel(struct ieee80211com *ic) { } static void iwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) { } static void iwm_scan_mindwell(struct ieee80211_scan_state *ss) { return; } void iwm_init_task(void *arg1) { struct iwm_softc *sc = arg1; IWM_LOCK(sc); while (sc->sc_flags & IWM_FLAG_BUSY) msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0); sc->sc_flags |= IWM_FLAG_BUSY; iwm_stop(sc); if (sc->sc_ic.ic_nrunning > 0) iwm_init(sc); sc->sc_flags &= ~IWM_FLAG_BUSY; wakeup(&sc->sc_flags); IWM_UNLOCK(sc); } static int iwm_resume(device_t dev) { struct iwm_softc *sc = device_get_softc(dev); int do_reinit = 0; uint16_t reg; /* Clear device-specific "PCI retry timeout" register (41h). */ reg = pci_read_config(dev, 0x40, sizeof(reg)); pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); iwm_init_task(device_get_softc(dev)); IWM_LOCK(sc); - if (sc->sc_flags & IWM_FLAG_DORESUME) { - sc->sc_flags &= ~IWM_FLAG_DORESUME; + if (sc->sc_flags & IWM_FLAG_SCANNING) { + sc->sc_flags &= ~IWM_FLAG_SCANNING; do_reinit = 1; } IWM_UNLOCK(sc); if (do_reinit) ieee80211_resume_all(&sc->sc_ic); return 0; } static int iwm_suspend(device_t dev) { int do_stop = 0; struct iwm_softc *sc = device_get_softc(dev); do_stop = !! (sc->sc_ic.ic_nrunning > 0); ieee80211_suspend_all(&sc->sc_ic); if (do_stop) { IWM_LOCK(sc); iwm_stop(sc); - sc->sc_flags |= IWM_FLAG_DORESUME; + sc->sc_flags |= IWM_FLAG_SCANNING; IWM_UNLOCK(sc); } return (0); } static int iwm_detach_local(struct iwm_softc *sc, int do_net80211) { struct iwm_fw_info *fw = &sc->sc_fw; device_t dev = sc->sc_dev; int i; ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task); callout_drain(&sc->sc_led_blink_to); callout_drain(&sc->sc_watchdog_to); iwm_stop_device(sc); - if (do_net80211) + if (do_net80211) { ieee80211_ifdetach(&sc->sc_ic); + } iwm_phy_db_free(sc); /* Free descriptor rings */ iwm_free_rx_ring(sc, &sc->rxq); for (i = 0; i < nitems(sc->txq); i++) iwm_free_tx_ring(sc, &sc->txq[i]); /* Free firmware */ if (fw->fw_fp != NULL) iwm_fw_info_free(fw); /* Free scheduler */ iwm_free_sched(sc); if (sc->ict_dma.vaddr != NULL) iwm_free_ict(sc); if (sc->kw_dma.vaddr != NULL) iwm_free_kw(sc); if (sc->fw_dma.vaddr != NULL) iwm_free_fwmem(sc); /* Finished with the hardware - detach things */ iwm_pci_detach(dev); mbufq_drain(&sc->sc_snd); IWM_LOCK_DESTROY(sc); return (0); } static int iwm_detach(device_t dev) { struct iwm_softc *sc = device_get_softc(dev); return (iwm_detach_local(sc, 1)); } static device_method_t iwm_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, iwm_probe), DEVMETHOD(device_attach, iwm_attach), DEVMETHOD(device_detach, iwm_detach), DEVMETHOD(device_suspend, iwm_suspend), DEVMETHOD(device_resume, iwm_resume), DEVMETHOD_END }; static driver_t iwm_pci_driver = { "iwm", iwm_pci_methods, sizeof (struct iwm_softc) }; static devclass_t iwm_devclass; DRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL); MODULE_DEPEND(iwm, firmware, 1, 1, 1); MODULE_DEPEND(iwm, pci, 1, 1, 1); MODULE_DEPEND(iwm, wlan, 1, 1, 1); Index: head/sys/dev/iwm/if_iwm_led.c =================================================================== --- head/sys/dev/iwm/if_iwm_led.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_led.c (revision 303327) @@ -1,182 +1,182 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Set led register on */ void iwm_mvm_led_enable(struct iwm_softc *sc) { IWM_WRITE(sc, IWM_CSR_LED_REG, IWM_CSR_LED_REG_TURN_ON); } /* Set led register off */ void iwm_mvm_led_disable(struct iwm_softc *sc) { IWM_WRITE(sc, IWM_CSR_LED_REG, IWM_CSR_LED_REG_TURN_OFF); } -int +static int iwm_mvm_led_is_enabled(struct iwm_softc *sc) { return (IWM_READ(sc, IWM_CSR_LED_REG) == IWM_CSR_LED_REG_TURN_ON); } -void +static void iwm_led_blink_timeout(void *arg) { struct iwm_softc *sc = arg; if (iwm_mvm_led_is_enabled(sc)) iwm_mvm_led_disable(sc); else iwm_mvm_led_enable(sc); callout_reset(&sc->sc_led_blink_to, (200 * hz) / 1000, iwm_led_blink_timeout, sc); } void iwm_led_blink_start(struct iwm_softc *sc) { callout_reset(&sc->sc_led_blink_to, 0, iwm_led_blink_timeout, sc); } void iwm_led_blink_stop(struct iwm_softc *sc) { - callout_drain(&sc->sc_led_blink_to); + callout_stop(&sc->sc_led_blink_to); iwm_mvm_led_disable(sc); } Index: head/sys/dev/iwm/if_iwm_led.h =================================================================== --- head/sys/dev/iwm/if_iwm_led.h (revision 303326) +++ head/sys/dev/iwm/if_iwm_led.h (revision 303327) @@ -1,101 +1,99 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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. * * $FreeBSD$ */ #ifndef __IF_IWM_LED_H__ #define __IF_IWM_LED_H__ -void iwm_mvm_led_enable(struct iwm_softc *); -void iwm_mvm_led_disable(struct iwm_softc *); -int iwm_mvm_led_is_enabled(struct iwm_softc *); -void iwm_led_blink_timeout(void *); -void iwm_led_blink_start(struct iwm_softc *); -void iwm_led_blink_stop(struct iwm_softc *); +extern void iwm_mvm_led_enable(struct iwm_softc *); +extern void iwm_mvm_led_disable(struct iwm_softc *); +extern void iwm_led_blink_start(struct iwm_softc *); +extern void iwm_led_blink_stop(struct iwm_softc *); #endif /* __IF_IWM_LED_H__ */ Index: head/sys/dev/iwm/if_iwm_mac_ctxt.c =================================================================== --- head/sys/dev/iwm/if_iwm_mac_ctxt.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_mac_ctxt.c (revision 303327) @@ -1,535 +1,537 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 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, vap->iv_myaddr); /* * XXX should we error out if in_assoc is 1 and ni == NULL? */ +#if 0 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)); + IEEE80211_ADDR_COPY(cmd->bssid_addr, ieee80211broadcastaddr); } +#else + /* + * XXX This workaround makes the firmware behave more correctly once + * we are associated, regularly giving us statistics notifications, + * as well as signaling missed beacons to us. + * Since we only call iwm_mvm_mac_ctxt_add() and + * iwm_mvm_mac_ctxt_changed() when already authenticating or + * associating, ni->ni_bssid should always make sense here. + */ + IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); +#endif /* * 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 = 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); + return iwm_mvm_mac_ctxt_cmd_station(sc, vap, action); } 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); + return iwm_mvm_mac_ctx_send(sc, vap, IWM_FW_CTXT_ACTION_MODIFY); } #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 Index: head/sys/dev/iwm/if_iwm_pcie_trans.c =================================================================== --- head/sys/dev/iwm/if_iwm_pcie_trans.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_pcie_trans.c (revision 303327) @@ -1,568 +1,581 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * This is a subset of what's in linux iwlwifi/pcie/trans.c. * The rest can be migrated out into here once they're no longer in * if_iwm.c. */ /* * basic device access */ uint32_t iwm_read_prph(struct iwm_softc *sc, uint32_t addr) { IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_RADDR, ((addr & 0x000fffff) | (3 << 24))); IWM_BARRIER_READ_WRITE(sc); return IWM_READ(sc, IWM_HBUS_TARG_PRPH_RDAT); } void iwm_write_prph(struct iwm_softc *sc, uint32_t addr, uint32_t val) { IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WADDR, ((addr & 0x000fffff) | (3 << 24))); IWM_BARRIER_WRITE(sc); IWM_WRITE(sc, IWM_HBUS_TARG_PRPH_WDAT, val); } #ifdef IWM_DEBUG /* iwlwifi: pcie/trans.c */ int iwm_read_mem(struct iwm_softc *sc, uint32_t addr, void *buf, int dwords) { int offs, ret = 0; uint32_t *vals = buf; if (iwm_nic_lock(sc)) { IWM_WRITE(sc, IWM_HBUS_TARG_MEM_RADDR, addr); for (offs = 0; offs < dwords; offs++) vals[offs] = IWM_READ(sc, IWM_HBUS_TARG_MEM_RDAT); iwm_nic_unlock(sc); } else { ret = EBUSY; } return ret; } #endif /* iwlwifi: pcie/trans.c */ int iwm_write_mem(struct iwm_softc *sc, uint32_t addr, const void *buf, int dwords) { int offs; const uint32_t *vals = buf; if (iwm_nic_lock(sc)) { IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WADDR, addr); /* WADDR auto-increments */ for (offs = 0; offs < dwords; offs++) { uint32_t val = vals ? vals[offs] : 0; IWM_WRITE(sc, IWM_HBUS_TARG_MEM_WDAT, val); } iwm_nic_unlock(sc); } else { IWM_DPRINTF(sc, IWM_DEBUG_TRANS, "%s: write_mem failed\n", __func__); return EBUSY; } return 0; } int iwm_write_mem32(struct iwm_softc *sc, uint32_t addr, uint32_t val) { return iwm_write_mem(sc, addr, &val, 1); } int iwm_poll_bit(struct iwm_softc *sc, int reg, uint32_t bits, uint32_t mask, int timo) { for (;;) { if ((IWM_READ(sc, reg) & mask) == (bits & mask)) { return 1; } if (timo < 10) { return 0; } timo -= 10; DELAY(10); } } int iwm_nic_lock(struct iwm_softc *sc) { int rv = 0; IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) + DELAY(2); + if (iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP, 15000)) { - rv = 1; + rv = 1; } else { /* jolt */ + IWM_DPRINTF(sc, IWM_DEBUG_RESET, + "%s: resetting device via NMI\n", __func__); IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_FORCE_NMI); } return rv; } void iwm_nic_unlock(struct iwm_softc *sc) { IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); } void iwm_set_bits_mask_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits, uint32_t mask) { uint32_t val; /* XXX: no error path? */ if (iwm_nic_lock(sc)) { val = iwm_read_prph(sc, reg) & mask; val |= bits; iwm_write_prph(sc, reg, val); iwm_nic_unlock(sc); } } void iwm_set_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) { iwm_set_bits_mask_prph(sc, reg, bits, ~0); } void iwm_clear_bits_prph(struct iwm_softc *sc, uint32_t reg, uint32_t bits) { iwm_set_bits_mask_prph(sc, reg, 0, ~bits); } /* * High-level hardware frobbing routines */ void iwm_enable_rfkill_int(struct iwm_softc *sc) { sc->sc_intmask = IWM_CSR_INT_BIT_RF_KILL; IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); } int iwm_check_rfkill(struct iwm_softc *sc) { uint32_t v; int rv; /* * "documentation" is not really helpful here: * 27: HW_RF_KILL_SW * Indicates state of (platform's) hardware RF-Kill switch * * But apparently when it's off, it's on ... */ v = IWM_READ(sc, IWM_CSR_GP_CNTRL); rv = (v & IWM_CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW) == 0; if (rv) { sc->sc_flags |= IWM_FLAG_RFKILL; } else { sc->sc_flags &= ~IWM_FLAG_RFKILL; } return rv; } #define IWM_HW_READY_TIMEOUT 50 int iwm_set_hw_ready(struct iwm_softc *sc) { + int ready; + IWM_SETBITS(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - return iwm_poll_bit(sc, IWM_CSR_HW_IF_CONFIG_REG, + ready = iwm_poll_bit(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, IWM_HW_READY_TIMEOUT); + if (ready) { + IWM_SETBITS(sc, IWM_CSR_MBOX_SET_REG, + IWM_CSR_MBOX_SET_REG_OS_ALIVE); + } + return ready; } #undef IWM_HW_READY_TIMEOUT int iwm_prepare_card_hw(struct iwm_softc *sc) { int rv = 0; int t = 0; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "->%s\n", __func__); if (iwm_set_hw_ready(sc)) goto out; DELAY(100); /* If HW is not ready, prepare the conditions to check again */ IWM_SETBITS(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_PREPARE); do { if (iwm_set_hw_ready(sc)) goto out; DELAY(200); t += 200; } while (t < 150000); rv = ETIMEDOUT; out: IWM_DPRINTF(sc, IWM_DEBUG_RESET, "<-%s\n", __func__); return rv; } void iwm_apm_config(struct iwm_softc *sc) { uint16_t reg; reg = pci_read_config(sc->sc_dev, PCIER_LINK_CTL, sizeof(reg)); if (reg & PCIEM_LINK_CTL_ASPMC_L1) { /* Um the Linux driver prints "Disabling L0S for this one ... */ IWM_SETBITS(sc, IWM_CSR_GIO_REG, IWM_CSR_GIO_REG_VAL_L0S_ENABLED); } else { /* ... and "Enabling" here */ IWM_CLRBITS(sc, IWM_CSR_GIO_REG, IWM_CSR_GIO_REG_VAL_L0S_ENABLED); } } /* * Start up NIC's basic functionality after it has been reset * (e.g. after platform boot, or shutdown via iwm_pcie_apm_stop()) * NOTE: This does not load uCode nor start the embedded processor */ int iwm_apm_init(struct iwm_softc *sc) { int error = 0; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "iwm apm start\n"); /* Disable L0S exit timer (platform NMI Work/Around) */ - IWM_SETBITS(sc, IWM_CSR_GIO_CHICKEN_BITS, - IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { + IWM_SETBITS(sc, IWM_CSR_GIO_CHICKEN_BITS, + IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); + } /* * Disable L0s without affecting L1; * don't wait for ICH L0s (ICH bug W/A) */ IWM_SETBITS(sc, IWM_CSR_GIO_CHICKEN_BITS, IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX); /* Set FH wait threshold to maximum (HW error during stress W/A) */ IWM_SETBITS(sc, IWM_CSR_DBG_HPET_MEM_REG, IWM_CSR_DBG_HPET_MEM_REG_VAL); /* * Enable HAP INTA (interrupt from management bus) to * wake device's PCI Express link L1a -> L0s */ IWM_SETBITS(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A); iwm_apm_config(sc); -#if 0 /* not for 7k */ +#if 0 /* not for 7k/8k */ /* Configure analog phase-lock-loop before activating to D0A */ if (trans->cfg->base_params->pll_cfg_val) IWM_SETBITS(trans, IWM_CSR_ANA_PLL_CFG, trans->cfg->base_params->pll_cfg_val); #endif /* * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); /* * Wait for clock stabilization; once stabilized, access to * device-internal resources is supported, e.g. iwm_write_prph() * and accesses to uCode SRAM. */ if (!iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 25000)) { device_printf(sc->sc_dev, "timeout waiting for clock stabilization\n"); error = ETIMEDOUT; goto out; } if (sc->host_interrupt_operation_mode) { /* * This is a bit of an abuse - This is needed for 7260 / 3160 * only check host_interrupt_operation_mode even if this is * not related to host_interrupt_operation_mode. * * Enable the oscillator to count wake up time for L1 exit. This * consumes slightly more power (100uA) - but allows to be sure * that we wake up from L1 on time. * * This looks weird: read twice the same register, discard the * value, set a bit, and yet again, read that same register * just to discard the value. But that's the way the hardware * seems to like it. */ iwm_read_prph(sc, IWM_OSC_CLK); iwm_read_prph(sc, IWM_OSC_CLK); iwm_set_bits_prph(sc, IWM_OSC_CLK, IWM_OSC_CLK_FORCE_CONTROL); iwm_read_prph(sc, IWM_OSC_CLK); iwm_read_prph(sc, IWM_OSC_CLK); } /* * Enable DMA clock and wait for it to stabilize. * * Write to "CLK_EN_REG"; "1" bits enable clocks, while "0" bits * do not disable clocks. This preserves any hardware bits already * set by default in "CLK_CTRL_REG" after reset. */ - iwm_write_prph(sc, IWM_APMG_CLK_EN_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); - //kpause("iwmapm", 0, mstohz(20), NULL); - DELAY(20); + if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { + iwm_write_prph(sc, IWM_APMG_CLK_EN_REG, + IWM_APMG_CLK_VAL_DMA_CLK_RQT); + DELAY(20); - /* Disable L1-Active */ - iwm_set_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, - IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); + /* Disable L1-Active */ + iwm_set_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, + IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); - /* Clear the interrupt in APMG if the NIC is in RFKILL */ - iwm_write_prph(sc, IWM_APMG_RTC_INT_STT_REG, - IWM_APMG_RTC_INT_STT_RFKILL); - + /* Clear the interrupt in APMG if the NIC is in RFKILL */ + iwm_write_prph(sc, IWM_APMG_RTC_INT_STT_REG, + IWM_APMG_RTC_INT_STT_RFKILL); + } out: if (error) device_printf(sc->sc_dev, "apm init error %d\n", error); return error; } /* iwlwifi/pcie/trans.c */ void iwm_apm_stop(struct iwm_softc *sc) { /* stop device's busmaster DMA activity */ IWM_SETBITS(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_STOP_MASTER); if (!iwm_poll_bit(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_MASTER_DISABLED, IWM_CSR_RESET_REG_FLAG_MASTER_DISABLED, 100)) device_printf(sc->sc_dev, "timeout waiting for master\n"); IWM_DPRINTF(sc, IWM_DEBUG_TRANS, "%s: iwm apm stop\n", __func__); } /* iwlwifi pcie/trans.c */ int iwm_start_hw(struct iwm_softc *sc) { int error; if ((error = iwm_prepare_card_hw(sc)) != 0) return error; /* Reset the entire device */ - IWM_WRITE(sc, IWM_CSR_RESET, - IWM_CSR_RESET_REG_FLAG_SW_RESET | - IWM_CSR_RESET_REG_FLAG_NEVO_RESET); + IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); DELAY(10); if ((error = iwm_apm_init(sc)) != 0) return error; iwm_enable_rfkill_int(sc); iwm_check_rfkill(sc); return 0; } /* iwlwifi pcie/trans.c (always main power) */ void iwm_set_pwr(struct iwm_softc *sc) { iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, IWM_APMG_PS_CTRL_VAL_PWR_SRC_VMAIN, ~IWM_APMG_PS_CTRL_MSK_PWR_SRC); } /* iwlwifi pcie/rx.c */ int iwm_pcie_rx_stop(struct iwm_softc *sc) { IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); return (iwm_poll_bit(sc, IWM_FH_MEM_RSSR_RX_STATUS_REG, IWM_FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, IWM_FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE, 1000)); } Index: head/sys/dev/iwm/if_iwm_phy_ctxt.c =================================================================== --- head/sys/dev/iwm/if_iwm_phy_ctxt.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_phy_ctxt.c (revision 303327) @@ -1,307 +1,307 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * BEGIN iwlwifi/mvm/phy-ctxt.c */ /* * Construct the generic fields of the PHY context command */ static void iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, struct iwm_phy_context_cmd *cmd, uint32_t action, uint32_t apply_time) { memset(cmd, 0, sizeof(struct iwm_phy_context_cmd)); IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD, "%s: id=%d, colour=%d, action=%d, apply_time=%d\n", __func__, ctxt->id, ctxt->color, action, apply_time); cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ctxt->id, ctxt->color)); cmd->action = htole32(action); cmd->apply_time = htole32(apply_time); } /* * Add the phy configuration to the PHY context command */ static void iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *sc, struct iwm_phy_context_cmd *cmd, struct ieee80211_channel *chan, uint8_t chains_static, uint8_t chains_dynamic) { struct ieee80211com *ic = &sc->sc_ic; uint8_t active_cnt, idle_cnt; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD, "%s: 2ghz=%d, channel=%d, chains static=0x%x, dynamic=0x%x, " "rx_ant=0x%x, tx_ant=0x%x\n", __func__, !! IEEE80211_IS_CHAN_2GHZ(chan), ieee80211_chan2ieee(ic, chan), chains_static, chains_dynamic, - IWM_FW_VALID_RX_ANT(sc), - IWM_FW_VALID_TX_ANT(sc)); + iwm_fw_valid_rx_ant(sc), + iwm_fw_valid_tx_ant(sc)); cmd->ci.band = IEEE80211_IS_CHAN_2GHZ(chan) ? IWM_PHY_BAND_24 : IWM_PHY_BAND_5; cmd->ci.channel = ieee80211_chan2ieee(ic, chan); cmd->ci.width = IWM_PHY_VHT_CHANNEL_MODE20; cmd->ci.ctrl_pos = IWM_PHY_VHT_CTRL_POS_1_BELOW; /* Set rx the chains */ idle_cnt = chains_static; active_cnt = chains_dynamic; - cmd->rxchain_info = htole32(IWM_FW_VALID_RX_ANT(sc) << + cmd->rxchain_info = htole32(iwm_fw_valid_rx_ant(sc) << IWM_PHY_RX_CHAIN_VALID_POS); cmd->rxchain_info |= htole32(idle_cnt << IWM_PHY_RX_CHAIN_CNT_POS); cmd->rxchain_info |= htole32(active_cnt << IWM_PHY_RX_CHAIN_MIMO_CNT_POS); - cmd->txchain_info = htole32(IWM_FW_VALID_TX_ANT(sc)); + cmd->txchain_info = htole32(iwm_fw_valid_tx_ant(sc)); } /* * Send a command * only if something in the configuration changed: in case that this is the * first time that the phy configuration is applied or in case that the phy * configuration changed from the previous apply. */ static int iwm_mvm_phy_ctxt_apply(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, uint8_t chains_static, uint8_t chains_dynamic, uint32_t action, uint32_t apply_time) { struct iwm_phy_context_cmd cmd; int ret; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD, "%s: called; channel=%p\n", __func__, ctxt->channel); /* Set the command header fields */ iwm_mvm_phy_ctxt_cmd_hdr(sc, ctxt, &cmd, action, apply_time); /* Set the command data */ iwm_mvm_phy_ctxt_cmd_data(sc, &cmd, ctxt->channel, chains_static, chains_dynamic); ret = iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONTEXT_CMD, IWM_CMD_SYNC, sizeof(struct iwm_phy_context_cmd), &cmd); if (ret) { device_printf(sc->sc_dev, "PHY ctxt cmd error. ret=%d\n", ret); } return ret; } /* * Send a command to add a PHY context based on the current HW configuration. */ int iwm_mvm_phy_ctxt_add(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, struct ieee80211_channel *chan, uint8_t chains_static, uint8_t chains_dynamic) { ctxt->channel = chan; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD, "%s: called; channel=%d\n", __func__, ieee80211_chan2ieee(&sc->sc_ic, chan)); return iwm_mvm_phy_ctxt_apply(sc, ctxt, chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_ADD, 0); } /* * Send a command to modify the PHY context based on the current HW * configuration. Note that the function does not check that the configuration * changed. */ int iwm_mvm_phy_ctxt_changed(struct iwm_softc *sc, struct iwm_mvm_phy_ctxt *ctxt, struct ieee80211_channel *chan, uint8_t chains_static, uint8_t chains_dynamic) { ctxt->channel = chan; IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_CMD, "%s: called; channel=%d\n", __func__, ieee80211_chan2ieee(&sc->sc_ic, chan)); return iwm_mvm_phy_ctxt_apply(sc, ctxt, chains_static, chains_dynamic, IWM_FW_CTXT_ACTION_MODIFY, 0); } /* * END iwlwifi/mvm/phy-ctxt.c */ Index: head/sys/dev/iwm/if_iwm_phy_db.c =================================================================== --- head/sys/dev/iwm/if_iwm_phy_db.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_phy_db.c (revision 303327) @@ -1,483 +1,483 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * BEGIN iwl-phy-db.c */ /* * get phy db section: returns a pointer to a phy db section specified by * type and channel group id. */ static struct iwm_phy_db_entry * iwm_phy_db_get_section(struct iwm_softc *sc, enum iwm_phy_db_section_type type, uint16_t chg_id) { struct iwm_phy_db *phy_db = &sc->sc_phy_db; if (type >= IWM_PHY_DB_MAX) return NULL; switch (type) { case IWM_PHY_DB_CFG: return &phy_db->cfg; case IWM_PHY_DB_CALIB_NCH: return &phy_db->calib_nch; case IWM_PHY_DB_CALIB_CHG_PAPD: if (chg_id >= IWM_NUM_PAPD_CH_GROUPS) return NULL; return &phy_db->calib_ch_group_papd[chg_id]; case IWM_PHY_DB_CALIB_CHG_TXP: if (chg_id >= IWM_NUM_TXP_CH_GROUPS) return NULL; return &phy_db->calib_ch_group_txp[chg_id]; default: return NULL; } return NULL; } int iwm_phy_db_set_section(struct iwm_softc *sc, struct iwm_calib_res_notif_phy_db *phy_db_notif) { enum iwm_phy_db_section_type type = le16toh(phy_db_notif->type); uint16_t size = le16toh(phy_db_notif->length); struct iwm_phy_db_entry *entry; uint16_t chg_id = 0; if (type == IWM_PHY_DB_CALIB_CHG_PAPD || type == IWM_PHY_DB_CALIB_CHG_TXP) chg_id = le16toh(*(uint16_t *)phy_db_notif->data); entry = iwm_phy_db_get_section(sc, type, chg_id); if (!entry) return EINVAL; if (entry->data) free(entry->data, M_DEVBUF); entry->data = malloc(size, M_DEVBUF, M_NOWAIT); if (!entry->data) { entry->size = 0; return ENOMEM; } memcpy(entry->data, phy_db_notif->data, size); entry->size = size; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "%s(%d): [PHYDB]SET: Type %d , Size: %d, data: %p\n", __func__, __LINE__, type, size, entry->data); return 0; } static int iwm_is_valid_channel(uint16_t ch_id) { if (ch_id <= 14 || (36 <= ch_id && ch_id <= 64 && ch_id % 4 == 0) || (100 <= ch_id && ch_id <= 140 && ch_id % 4 == 0) || (145 <= ch_id && ch_id <= 165 && ch_id % 4 == 1)) return 1; return 0; } static uint8_t iwm_ch_id_to_ch_index(uint16_t ch_id) { if (!iwm_is_valid_channel(ch_id)) return 0xff; if (ch_id <= 14) return ch_id - 1; if (ch_id <= 64) return (ch_id + 20) / 4; if (ch_id <= 140) return (ch_id - 12) / 4; return (ch_id - 13) / 4; } static uint16_t iwm_channel_id_to_papd(uint16_t ch_id) { if (!iwm_is_valid_channel(ch_id)) return 0xff; if (1 <= ch_id && ch_id <= 14) return 0; if (36 <= ch_id && ch_id <= 64) return 1; if (100 <= ch_id && ch_id <= 140) return 2; return 3; } static uint16_t iwm_channel_id_to_txp(struct iwm_softc *sc, uint16_t ch_id) { struct iwm_phy_db *phy_db = &sc->sc_phy_db; struct iwm_phy_db_chg_txp *txp_chg; int i; uint8_t ch_index = iwm_ch_id_to_ch_index(ch_id); if (ch_index == 0xff) return 0xff; for (i = 0; i < IWM_NUM_TXP_CH_GROUPS; i++) { txp_chg = (void *)phy_db->calib_ch_group_txp[i].data; if (!txp_chg) return 0xff; /* * Looking for the first channel group that its max channel is * higher then wanted channel. */ if (le16toh(txp_chg->max_channel_idx) >= ch_index) return i; } return 0xff; } static int iwm_phy_db_get_section_data(struct iwm_softc *sc, uint32_t type, uint8_t **data, uint16_t *size, uint16_t ch_id) { struct iwm_phy_db_entry *entry; uint16_t ch_group_id = 0; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "->%s\n", __func__); /* find wanted channel group */ if (type == IWM_PHY_DB_CALIB_CHG_PAPD) ch_group_id = iwm_channel_id_to_papd(ch_id); else if (type == IWM_PHY_DB_CALIB_CHG_TXP) ch_group_id = iwm_channel_id_to_txp(sc, ch_id); entry = iwm_phy_db_get_section(sc, type, ch_group_id); if (!entry) return EINVAL; *data = entry->data; *size = entry->size; IWM_DPRINTF(sc, IWM_DEBUG_RESET, "%s(%d): [PHYDB] GET: Type %d , Size: %d\n", __func__, __LINE__, type, *size); return 0; } static int iwm_send_phy_db_cmd(struct iwm_softc *sc, uint16_t type, uint16_t length, void *data) { struct iwm_phy_db_cmd phy_db_cmd; struct iwm_host_cmd cmd = { .id = IWM_PHY_DB_CMD, .flags = IWM_CMD_SYNC, }; IWM_DPRINTF(sc, IWM_DEBUG_CMD, "Sending PHY-DB hcmd of type %d, of length %d\n", type, length); /* Set phy db cmd variables */ phy_db_cmd.type = le16toh(type); phy_db_cmd.length = le16toh(length); /* Set hcmd variables */ cmd.data[0] = &phy_db_cmd; cmd.len[0] = sizeof(struct iwm_phy_db_cmd); cmd.data[1] = data; cmd.len[1] = length; - cmd.dataflags[1] = IWM_HCMD_DFL_NOCOPY; return iwm_send_cmd(sc, &cmd); } static int iwm_phy_db_send_all_channel_groups(struct iwm_softc *sc, enum iwm_phy_db_section_type type, uint8_t max_ch_groups) { uint16_t i; int err; struct iwm_phy_db_entry *entry; /* Send all the channel-specific groups to operational fw */ for (i = 0; i < max_ch_groups; i++) { entry = iwm_phy_db_get_section(sc, type, i); if (!entry) return EINVAL; if (!entry->size) continue; /* Send the requested PHY DB section */ err = iwm_send_phy_db_cmd(sc, type, entry->size, entry->data); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD, "%s: Can't SEND phy_db section %d (%d), " "err %d\n", __func__, type, i, err); return err; } + DELAY(1000); IWM_DPRINTF(sc, IWM_DEBUG_CMD, "Sent PHY_DB HCMD, type = %d num = %d\n", type, i); } return 0; } int iwm_send_phy_db_data(struct iwm_softc *sc) { uint8_t *data = NULL; uint16_t size = 0; int err; IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Sending phy db data and configuration to runtime image\n", __func__); /* Send PHY DB CFG section */ err = iwm_phy_db_get_section_data(sc, IWM_PHY_DB_CFG, &data, &size, 0); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot get Phy DB cfg section, %d\n", __func__, err); return err; } err = iwm_send_phy_db_cmd(sc, IWM_PHY_DB_CFG, size, data); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot send HCMD of Phy DB cfg section, %d\n", __func__, err); return err; } err = iwm_phy_db_get_section_data(sc, IWM_PHY_DB_CALIB_NCH, &data, &size, 0); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot get Phy DB non specific channel section, " "%d\n", __func__, err); return err; } err = iwm_send_phy_db_cmd(sc, IWM_PHY_DB_CALIB_NCH, size, data); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot send HCMD of Phy DB non specific channel " "sect, %d\n", __func__, err); return err; } /* Send all the TXP channel specific data */ err = iwm_phy_db_send_all_channel_groups(sc, IWM_PHY_DB_CALIB_CHG_PAPD, IWM_NUM_PAPD_CH_GROUPS); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot send channel specific PAPD groups, %d\n", __func__, err); return err; } /* Send all the TXP channel specific data */ err = iwm_phy_db_send_all_channel_groups(sc, IWM_PHY_DB_CALIB_CHG_TXP, IWM_NUM_TXP_CH_GROUPS); if (err) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Cannot send channel specific TX power groups, " "%d\n", __func__, err); return err; } IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Finished sending phy db non channel data\n", __func__); return 0; } static void iwm_phy_db_free_section(struct iwm_softc *sc, enum iwm_phy_db_section_type type, uint16_t chg_id) { struct iwm_phy_db_entry *entry = iwm_phy_db_get_section(sc, type, chg_id); if (!entry) return; if (entry->data != NULL) free(entry->data, M_DEVBUF); entry->data = NULL; entry->size = 0; } void iwm_phy_db_free(struct iwm_softc *sc) { int i; iwm_phy_db_free_section(sc, IWM_PHY_DB_CFG, 0); iwm_phy_db_free_section(sc, IWM_PHY_DB_CALIB_NCH, 0); for (i = 0; i < IWM_NUM_PAPD_CH_GROUPS; i++) iwm_phy_db_free_section(sc, IWM_PHY_DB_CALIB_CHG_PAPD, i); for (i = 0; i < IWM_NUM_TXP_CH_GROUPS; i++) iwm_phy_db_free_section(sc, IWM_PHY_DB_CALIB_CHG_TXP, i); } Index: head/sys/dev/iwm/if_iwm_power.c =================================================================== --- head/sys/dev/iwm/if_iwm_power.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_power.c (revision 303327) @@ -1,348 +1,337 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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. */ #include __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * BEGIN mvm/power.c */ #define IWM_POWER_KEEP_ALIVE_PERIOD_SEC 25 static int iwm_mvm_beacon_filter_send_cmd(struct iwm_softc *sc, struct iwm_beacon_filter_cmd *cmd) { int ret; ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_BEACON_FILTERING_CMD, IWM_CMD_SYNC, sizeof(struct iwm_beacon_filter_cmd), cmd); if (!ret) { IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "ba_enable_beacon_abort is: %d\n", le32toh(cmd->ba_enable_beacon_abort)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "ba_escape_timer is: %d\n", le32toh(cmd->ba_escape_timer)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_debug_flag is: %d\n", le32toh(cmd->bf_debug_flag)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_enable_beacon_filter is: %d\n", le32toh(cmd->bf_enable_beacon_filter)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_energy_delta is: %d\n", le32toh(cmd->bf_energy_delta)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_escape_timer is: %d\n", le32toh(cmd->bf_escape_timer)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_roaming_energy_delta is: %d\n", le32toh(cmd->bf_roaming_energy_delta)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_roaming_state is: %d\n", le32toh(cmd->bf_roaming_state)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_temp_threshold is: %d\n", le32toh(cmd->bf_temp_threshold)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_temp_fast_filter is: %d\n", le32toh(cmd->bf_temp_fast_filter)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "bf_temp_slow_filter is: %d\n", le32toh(cmd->bf_temp_slow_filter)); } return ret; } static void iwm_mvm_beacon_filter_set_cqm_params(struct iwm_softc *sc, struct iwm_node *in, struct iwm_beacon_filter_cmd *cmd) { cmd->ba_enable_beacon_abort = htole32(sc->sc_bf.ba_enabled); } static int iwm_mvm_update_beacon_abort(struct iwm_softc *sc, struct iwm_node *in, int enable) { struct iwm_beacon_filter_cmd cmd = { IWM_BF_CMD_CONFIG_DEFAULTS, .bf_enable_beacon_filter = htole32(1), .ba_enable_beacon_abort = htole32(enable), }; if (!sc->sc_bf.bf_enabled) return 0; sc->sc_bf.ba_enabled = enable; iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); return iwm_mvm_beacon_filter_send_cmd(sc, &cmd); } static void iwm_mvm_power_log(struct iwm_softc *sc, struct iwm_mac_power_cmd *cmd) { IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "Sending power table command on mac id 0x%X for " "power level %d, flags = 0x%X\n", cmd->id_and_color, IWM_POWER_SCHEME_CAM, le16toh(cmd->flags)); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "Keep alive = %u sec\n", le16toh(cmd->keep_alive_seconds)); if (!(cmd->flags & htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK))) { IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "Disable power management\n"); return; } } static void iwm_mvm_power_build_cmd(struct iwm_softc *sc, struct iwm_node *in, struct iwm_mac_power_cmd *cmd) { struct ieee80211_node *ni = &in->in_ni; int dtimper, dtimper_msec; int keep_alive; struct ieee80211com *ic = &sc->sc_ic; struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); cmd->id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, IWM_DEFAULT_COLOR)); dtimper = vap->iv_dtim_period ?: 1; /* * Regardless of power management state the driver must set * keep alive period. FW will use it for sending keep alive NDPs * immediately after association. Check that keep alive period * is at least 3 * DTIM */ dtimper_msec = dtimper * ni->ni_intval; keep_alive = MAX(3 * dtimper_msec, 1000 * IWM_POWER_KEEP_ALIVE_PERIOD_SEC); keep_alive = roundup(keep_alive, 1000) / 1000; cmd->keep_alive_seconds = htole16(keep_alive); } int iwm_mvm_power_mac_update_mode(struct iwm_softc *sc, struct iwm_node *in) { int ret; int ba_enable; struct iwm_mac_power_cmd cmd; memset(&cmd, 0, sizeof(cmd)); iwm_mvm_power_build_cmd(sc, in, &cmd); iwm_mvm_power_log(sc, &cmd); if ((ret = iwm_mvm_send_cmd_pdu(sc, IWM_MAC_PM_POWER_TABLE, IWM_CMD_SYNC, sizeof(cmd), &cmd)) != 0) return ret; ba_enable = !!(cmd.flags & htole16(IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK)); return iwm_mvm_update_beacon_abort(sc, in, ba_enable); } int iwm_mvm_power_update_device(struct iwm_softc *sc) { struct iwm_device_power_cmd cmd = { .flags = htole16(IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK), }; if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD)) return 0; cmd.flags |= htole16(IWM_DEVICE_POWER_FLAGS_CAM_MSK); IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE | IWM_DEBUG_CMD, "Sending device power command with flags = 0x%X\n", cmd.flags); return iwm_mvm_send_cmd_pdu(sc, IWM_POWER_TABLE_CMD, IWM_CMD_SYNC, sizeof(cmd), &cmd); } int iwm_mvm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) { struct iwm_beacon_filter_cmd cmd = { IWM_BF_CMD_CONFIG_DEFAULTS, .bf_enable_beacon_filter = htole32(1), }; int ret; iwm_mvm_beacon_filter_set_cqm_params(sc, in, &cmd); ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); if (ret == 0) sc->sc_bf.bf_enabled = 1; return ret; } int iwm_mvm_disable_beacon_filter(struct iwm_softc *sc) { struct iwm_beacon_filter_cmd cmd; int ret; memset(&cmd, 0, sizeof(cmd)); if ((sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_BF_UPDATED) == 0) return 0; ret = iwm_mvm_beacon_filter_send_cmd(sc, &cmd); if (ret == 0) sc->sc_bf.bf_enabled = 0; return ret; } - -#if 0 -static int -iwm_mvm_update_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) -{ - if (!sc->sc_bf.bf_enabled) - return 0; - - return iwm_mvm_enable_beacon_filter(sc, in); -} -#endif Index: head/sys/dev/iwm/if_iwm_scan.c =================================================================== --- head/sys/dev/iwm/if_iwm_scan.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_scan.c (revision 303327) @@ -1,452 +1,735 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * BEGIN mvm/scan.c */ #define IWM_PLCP_QUIET_THRESH 1 #define IWM_ACTIVE_QUIET_TIME 10 #define LONG_OUT_TIME_PERIOD (600 * IEEE80211_DUR_TU) #define SHORT_OUT_TIME_PERIOD (200 * IEEE80211_DUR_TU) #define SUSPEND_TIME_PERIOD (100 * IEEE80211_DUR_TU) static uint16_t iwm_mvm_scan_rx_chain(struct iwm_softc *sc) { uint16_t rx_chain; uint8_t rx_ant; - rx_ant = IWM_FW_VALID_RX_ANT(sc); + rx_ant = iwm_fw_valid_rx_ant(sc); rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS; rx_chain |= 0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS; return htole16(rx_chain); } +#if 0 static uint32_t iwm_mvm_scan_max_out_time(struct iwm_softc *sc, uint32_t flags, int is_assoc) { if (!is_assoc) return 0; if (flags & 0x1) return htole32(SHORT_OUT_TIME_PERIOD); return htole32(LONG_OUT_TIME_PERIOD); } static uint32_t iwm_mvm_scan_suspend_time(struct iwm_softc *sc, int is_assoc) { if (!is_assoc) return 0; return htole32(SUSPEND_TIME_PERIOD); } +#endif static uint32_t -iwm_mvm_scan_rxon_flags(struct iwm_softc *sc, int flags) -{ - if (flags & IEEE80211_CHAN_2GHZ) - return htole32(IWM_PHY_BAND_24); - else - return htole32(IWM_PHY_BAND_5); -} - -static uint32_t iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck) { uint32_t tx_ant; int i, ind; for (i = 0, ind = sc->sc_scan_last_antenna; i < IWM_RATE_MCS_ANT_NUM; i++) { ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM; - if (IWM_FW_VALID_TX_ANT(sc) & (1 << ind)) { + if (iwm_fw_valid_tx_ant(sc) & (1 << ind)) { sc->sc_scan_last_antenna = ind; break; } } tx_ant = (1 << sc->sc_scan_last_antenna) << IWM_RATE_MCS_ANT_POS; if ((flags & IEEE80211_CHAN_2GHZ) && !no_cck) return htole32(IWM_RATE_1M_PLCP | IWM_RATE_MCS_CCK_MSK | tx_ant); else return htole32(IWM_RATE_6M_PLCP | tx_ant); } +#if 0 /* * If req->n_ssids > 0, it means we should do an active scan. * In case of active scan w/o directed scan, we receive a zero-length SSID * just to notify that this scan is active and not passive. * In order to notify the FW of the number of SSIDs we wish to scan (including * the zero-length one), we need to set the corresponding bits in chan->type, * one for each SSID, and set the active bit (first). If the first SSID is * already included in the probe template, so we need to set only * req->n_ssids - 1 bits in addition to the first bit. */ static uint16_t iwm_mvm_get_active_dwell(struct iwm_softc *sc, int flags, int n_ssids) { if (flags & IEEE80211_CHAN_2GHZ) return 30 + 3 * (n_ssids + 1); return 20 + 2 * (n_ssids + 1); } static uint16_t iwm_mvm_get_passive_dwell(struct iwm_softc *sc, int flags) { return (flags & IEEE80211_CHAN_2GHZ) ? 100 + 20 : 100 + 10; } +#endif static int -iwm_mvm_scan_fill_channels(struct iwm_softc *sc, struct iwm_scan_cmd *cmd, - int flags, int n_ssids, int basic_ssid) +iwm_mvm_scan_skip_channel(struct ieee80211_channel *c) { + if (IEEE80211_IS_CHAN_2GHZ(c) && IEEE80211_IS_CHAN_B(c)) + return 0; + else if (IEEE80211_IS_CHAN_5GHZ(c) && IEEE80211_IS_CHAN_A(c)) + return 0; + else + return 1; +} + +static uint8_t +iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc, + struct iwm_scan_channel_cfg_lmac *chan, int n_ssids) +{ struct ieee80211com *ic = &sc->sc_ic; - uint16_t passive_dwell = iwm_mvm_get_passive_dwell(sc, flags); - uint16_t active_dwell = iwm_mvm_get_active_dwell(sc, flags, n_ssids); - struct iwm_scan_channel *chan = (struct iwm_scan_channel *) - (cmd->data + le16toh(cmd->tx_cmd.len)); - int type = (1 << n_ssids) - 1; struct ieee80211_channel *c; - int nchan, j; + uint8_t nchan; + int j; - if (!basic_ssid) - type |= (1 << n_ssids); - - for (nchan = j = 0; j < ic->ic_nchans; j++) { + for (nchan = j = 0; + j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) { c = &ic->ic_channels[j]; /* For 2GHz, only populate 11b channels */ /* For 5GHz, only populate 11a channels */ /* * Catch other channels, in case we have 900MHz channels or * something in the chanlist. */ - if ((flags & IEEE80211_CHAN_2GHZ) && (! IEEE80211_IS_CHAN_B(c))) { + if (iwm_mvm_scan_skip_channel(c)) { + IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM, + "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n", + __func__, c->ic_freq, c->ic_ieee, c->ic_flags); continue; - } else if ((flags & IEEE80211_CHAN_5GHZ) && (! IEEE80211_IS_CHAN_A(c))) { - continue; - } else { + } + + IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM, + "Adding channel %d (%d Mhz) to the list\n", + nchan, c->ic_freq); + chan->channel_num = htole16(ieee80211_mhz2ieee(c->ic_freq, 0)); + chan->iter_count = htole16(1); + chan->iter_interval = htole32(0); + chan->flags = htole32(IWM_UNIFIED_SCAN_CHANNEL_PARTIAL); +#if 0 /* makes scanning while associated less useful */ + if (n_ssids != 0) + chan->flags |= htole32(1 << 1); /* select SSID 0 */ +#endif + chan++; + nchan++; + } + + return nchan; +} + +static uint8_t +iwm_mvm_umac_scan_fill_channels(struct iwm_softc *sc, + struct iwm_scan_channel_cfg_umac *chan, int n_ssids) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211_channel *c; + uint8_t nchan; + int j; + + for (nchan = j = 0; + j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) { + c = &ic->ic_channels[j]; + /* For 2GHz, only populate 11b channels */ + /* For 5GHz, only populate 11a channels */ + /* + * Catch other channels, in case we have 900MHz channels or + * something in the chanlist. + */ + if (iwm_mvm_scan_skip_channel(c)) { IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM, "%s: skipping channel (freq=%d, ieee=%d, flags=0x%08x)\n", - __func__, - c->ic_freq, - c->ic_ieee, - c->ic_flags); + __func__, c->ic_freq, c->ic_ieee, c->ic_flags); + continue; } + IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_EEPROM, "Adding channel %d (%d Mhz) to the list\n", - nchan, c->ic_freq); - chan->channel = htole16(ieee80211_mhz2ieee(c->ic_freq, flags)); - chan->type = htole32(type); - if (c->ic_flags & IEEE80211_CHAN_PASSIVE) - chan->type &= htole32(~IWM_SCAN_CHANNEL_TYPE_ACTIVE); - chan->active_dwell = htole16(active_dwell); - chan->passive_dwell = htole16(passive_dwell); - chan->iteration_count = htole16(1); + nchan, c->ic_freq); + chan->channel_num = ieee80211_mhz2ieee(c->ic_freq, 0); + chan->iter_count = 1; + chan->iter_interval = htole16(0); + chan->flags = htole32(0); +#if 0 /* makes scanning while associated less useful */ + if (n_ssids != 0) + chan->flags = htole32(1 << 0); /* select SSID 0 */ +#endif chan++; nchan++; } - if (nchan == 0) - device_printf(sc->sc_dev, - "%s: NO CHANNEL!\n", __func__); + return nchan; } -/* - * Fill in probe request with the following parameters: - * TA is our vif HW address, which mac80211 ensures we have. - * Packet is broadcasted, so this is both SA and DA. - * The probe request IE is made out of two: first comes the most prioritized - * SSID if a directed scan is requested. Second comes whatever extra - * information was given to us as the scan request IE. - */ -static uint16_t -iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct ieee80211_frame *frame, - const uint8_t *ta, int n_ssids, const uint8_t *ssid, int ssid_len, - const uint8_t *ie, int ie_len, int left) +static int +iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq) { - uint8_t *pos = NULL; + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf; + struct ieee80211_rateset *rs; + size_t remain = sizeof(preq->buf); + uint8_t *frm, *pos; + int ssid_len = 0; + const uint8_t *ssid = NULL; - /* Make sure there is enough space for the probe request, - * two mandatory IEs and the data */ - left -= sizeof(*frame); - if (left < 0) - return 0; + memset(preq, 0, sizeof(*preq)); - frame->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + /* Ensure enough space for header and SSID IE. */ + if (remain < sizeof(*wh) + 2 + ssid_len) + return ENOBUFS; + + /* + * Build a probe request frame. Most of the following code is a + * copy & paste of what is done in net80211. + */ + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | IEEE80211_FC0_SUBTYPE_PROBE_REQ; - frame->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(frame->i_addr1, ieee80211broadcastaddr); - IEEE80211_ADDR_COPY(frame->i_addr2, ta); - IEEE80211_ADDR_COPY(frame->i_addr3, ieee80211broadcastaddr); + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(wh->i_addr1, ieee80211broadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, vap ? vap->iv_myaddr : ic->ic_macaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, ieee80211broadcastaddr); + *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ + *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ - /* for passive scans, no need to fill anything */ - if (n_ssids == 0) - return sizeof(*frame); + frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, ssid, ssid_len); - /* points to the payload of the request */ - pos = (uint8_t *)frame + sizeof(*frame); + /* Tell the firmware where the MAC header is. */ + preq->mac_header.offset = 0; + preq->mac_header.len = htole16(frm - (uint8_t *)wh); + remain -= frm - (uint8_t *)wh; - /* fill in our SSID IE */ - left -= ssid_len + 2; - if (left < 0) - return 0; + /* Fill in 2GHz IEs and tell firmware where they are. */ + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + if (remain < 4 + rs->rs_nrates) + return ENOBUFS; + } else if (remain < 2 + rs->rs_nrates) { + return ENOBUFS; + } + preq->band_data[0].offset = htole16(frm - (uint8_t *)wh); + pos = frm; + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + preq->band_data[0].len = htole16(frm - pos); + remain -= frm - pos; - pos = ieee80211_add_ssid(pos, ssid, ssid_len); + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) { + if (remain < 3) + return ENOBUFS; + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = 0; + remain -= 3; + } - if (ie && ie_len && left >= ie_len) { - memcpy(pos, ie, ie_len); - pos += ie_len; + if (sc->sc_nvm.sku_cap_band_52GHz_enable) { + /* Fill in 5GHz IEs. */ + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + if (remain < 4 + rs->rs_nrates) + return ENOBUFS; + } else if (remain < 2 + rs->rs_nrates) { + return ENOBUFS; + } + preq->band_data[1].offset = htole16(frm - (uint8_t *)wh); + pos = frm; + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + preq->band_data[1].len = htole16(frm - pos); + remain -= frm - pos; } - return pos - (uint8_t *)frame; + /* Send 11n IEs on both 2GHz and 5GHz bands. */ + preq->common_data.offset = htole16(frm - (uint8_t *)wh); + pos = frm; +#if 0 + if (ic->ic_flags & IEEE80211_F_HTON) { + if (remain < 28) + return ENOBUFS; + frm = ieee80211_add_htcaps(frm, ic); + /* XXX add WME info? */ + } +#endif + preq->common_data.len = htole16(frm - pos); + + return 0; } int -iwm_mvm_scan_request(struct iwm_softc *sc, int flags, - int n_ssids, uint8_t *ssid, int ssid_len) +iwm_mvm_config_umac_scan(struct iwm_softc *sc) { + struct ieee80211com *ic = &sc->sc_ic; + struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); + + struct iwm_scan_config *scan_config; + int ret, j, nchan; + size_t cmd_size; + struct ieee80211_channel *c; struct iwm_host_cmd hcmd = { - .id = IWM_SCAN_REQUEST_CMD, + .id = iwm_cmd_id(IWM_SCAN_CFG_CMD, IWM_ALWAYS_LONG_GROUP, 0), + .flags = IWM_CMD_SYNC, + }; + static const uint32_t rates = (IWM_SCAN_CONFIG_RATE_1M | + IWM_SCAN_CONFIG_RATE_2M | IWM_SCAN_CONFIG_RATE_5M | + IWM_SCAN_CONFIG_RATE_11M | IWM_SCAN_CONFIG_RATE_6M | + IWM_SCAN_CONFIG_RATE_9M | IWM_SCAN_CONFIG_RATE_12M | + IWM_SCAN_CONFIG_RATE_18M | IWM_SCAN_CONFIG_RATE_24M | + IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M | + IWM_SCAN_CONFIG_RATE_54M); + + cmd_size = sizeof(*scan_config) + sc->sc_capa_n_scan_channels; + + scan_config = malloc(cmd_size, M_DEVBUF, M_NOWAIT | M_ZERO); + if (scan_config == NULL) + return ENOMEM; + + scan_config->tx_chains = htole32(iwm_fw_valid_tx_ant(sc)); + scan_config->rx_chains = htole32(iwm_fw_valid_rx_ant(sc)); + scan_config->legacy_rates = htole32(rates | + IWM_SCAN_CONFIG_SUPPORTED_RATE(rates)); + + /* These timings correspond to iwlwifi's UNASSOC scan. */ + scan_config->dwell_active = 10; + scan_config->dwell_passive = 110; + scan_config->dwell_fragmented = 44; + scan_config->dwell_extended = 90; + scan_config->out_of_channel_time = htole32(0); + scan_config->suspend_time = htole32(0); + + IEEE80211_ADDR_COPY(scan_config->mac_addr, + vap ? vap->iv_myaddr : ic->ic_macaddr); + + scan_config->bcast_sta_id = sc->sc_aux_sta.sta_id; + scan_config->channel_flags = IWM_CHANNEL_FLAG_EBS | + IWM_CHANNEL_FLAG_ACCURATE_EBS | IWM_CHANNEL_FLAG_EBS_ADD | + IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; + + for (nchan = j = 0; + j < ic->ic_nchans && nchan < sc->sc_capa_n_scan_channels; j++) { + c = &ic->ic_channels[j]; + /* For 2GHz, only populate 11b channels */ + /* For 5GHz, only populate 11a channels */ + /* + * Catch other channels, in case we have 900MHz channels or + * something in the chanlist. + */ + if (iwm_mvm_scan_skip_channel(c)) + continue; + scan_config->channel_array[nchan++] = + ieee80211_mhz2ieee(c->ic_freq, 0); + } + + scan_config->flags = htole32(IWM_SCAN_CONFIG_FLAG_ACTIVATE | + IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS | + IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS | + IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS | + IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID | + IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES | + IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES | + IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR | + IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS| + IWM_SCAN_CONFIG_N_CHANNELS(nchan) | + IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED); + + hcmd.data[0] = scan_config; + hcmd.len[0] = cmd_size; + + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Sending UMAC scan config\n"); + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, + "UMAC scan config was sent successfully\n"); + + free(scan_config, M_DEVBUF); + return ret; +} + +int +iwm_mvm_umac_scan(struct iwm_softc *sc) +{ + struct iwm_host_cmd hcmd = { + .id = iwm_cmd_id(IWM_SCAN_REQ_UMAC, IWM_ALWAYS_LONG_GROUP, 0), .len = { 0, }, - .data = { sc->sc_scan_cmd, }, + .data = { NULL, }, .flags = IWM_CMD_SYNC, - .dataflags = { IWM_HCMD_DFL_NOCOPY, }, }; - struct iwm_scan_cmd *cmd = sc->sc_scan_cmd; - struct ieee80211com *ic = &sc->sc_ic; - struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); - int is_assoc = 0; + struct iwm_scan_req_umac *req; + struct iwm_scan_req_umac_tail *tail; + size_t req_len; + int ssid_len = 0; + const uint8_t *ssid = NULL; int ret; - uint32_t status; - int basic_ssid = - !(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID); - sc->sc_scanband = flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ); + req_len = sizeof(struct iwm_scan_req_umac) + + (sizeof(struct iwm_scan_channel_cfg_umac) * + sc->sc_capa_n_scan_channels) + + sizeof(struct iwm_scan_req_umac_tail); + if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE) + return ENOMEM; + req = malloc(req_len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (req == NULL) + return ENOMEM; - IWM_DPRINTF(sc, IWM_DEBUG_SCAN, - "Handling ieee80211 scan request\n"); - memset(cmd, 0, sc->sc_scan_cmd_len); + hcmd.len[0] = (uint16_t)req_len; + hcmd.data[0] = (void *)req; - cmd->quiet_time = htole16(IWM_ACTIVE_QUIET_TIME); - cmd->quiet_plcp_th = htole16(IWM_PLCP_QUIET_THRESH); - cmd->rxchain_sel_flags = iwm_mvm_scan_rx_chain(sc); - cmd->max_out_time = iwm_mvm_scan_max_out_time(sc, 0, is_assoc); - cmd->suspend_time = iwm_mvm_scan_suspend_time(sc, is_assoc); - cmd->rxon_flags = iwm_mvm_scan_rxon_flags(sc, flags); - cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP | - IWM_MAC_FILTER_IN_BEACON); + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Handling ieee80211 scan request\n"); - cmd->type = htole32(IWM_SCAN_TYPE_FORCED); - cmd->repeats = htole32(1); + /* These timings correspond to iwlwifi's UNASSOC scan. */ + req->active_dwell = 10; + req->passive_dwell = 110; + req->fragmented_dwell = 44; + req->extended_dwell = 90; + req->max_out_time = 0; + req->suspend_time = 0; - /* - * If the user asked for passive scan, don't change to active scan if - * you see any activity on the channel - remain passive. - */ - if (n_ssids > 0) { - cmd->passive2active = htole16(1); - cmd->scan_flags |= IWM_SCAN_FLAGS_PASSIVE2ACTIVE; -#if 0 - if (basic_ssid) { - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; - } -#endif + req->scan_priority = htole32(IWM_SCAN_PRIORITY_HIGH); + req->ooc_priority = htole32(IWM_SCAN_PRIORITY_HIGH); + + req->n_channels = iwm_mvm_umac_scan_fill_channels(sc, + (struct iwm_scan_channel_cfg_umac *)req->data, ssid_len != 0); + + req->general_flags = htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL | + IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE | + IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL); + + tail = (void *)((char *)&req->data + + sizeof(struct iwm_scan_channel_cfg_umac) * + sc->sc_capa_n_scan_channels); + + /* Check if we're doing an active directed scan. */ + if (ssid_len != 0) { + tail->direct_scan[0].id = IEEE80211_ELEMID_SSID; + tail->direct_scan[0].len = ssid_len; + memcpy(tail->direct_scan[0].ssid, ssid, ssid_len); + req->general_flags |= + htole32(IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT); } else { - cmd->passive2active = 0; - cmd->scan_flags &= ~IWM_SCAN_FLAGS_PASSIVE2ACTIVE; + req->general_flags |= htole32(IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE); } - cmd->tx_cmd.tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) + req->general_flags |= + htole32(IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED); + + ret = iwm_mvm_fill_probe_req(sc, &tail->preq); + if (ret) { + free(req, M_DEVBUF); + return ret; + } + + /* Specify the scan plan: We'll do one iteration. */ + tail->schedule[0].interval = 0; + tail->schedule[0].iter_count = 1; + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, + "Scan request was sent successfully\n"); + free(req, M_DEVBUF); + return ret; +} + +int +iwm_mvm_lmac_scan(struct iwm_softc *sc) +{ + struct iwm_host_cmd hcmd = { + .id = IWM_SCAN_OFFLOAD_REQUEST_CMD, + .len = { 0, }, + .data = { NULL, }, + .flags = IWM_CMD_SYNC, + }; + struct iwm_scan_req_lmac *req; + size_t req_len; + int ret; + int ssid_len = 0; + const uint8_t *ssid = NULL; + + IWM_DPRINTF(sc, IWM_DEBUG_SCAN, + "Handling ieee80211 scan request\n"); + + req_len = sizeof(struct iwm_scan_req_lmac) + + (sizeof(struct iwm_scan_channel_cfg_lmac) * + sc->sc_capa_n_scan_channels) + sizeof(struct iwm_scan_probe_req); + if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE) + return ENOMEM; + req = malloc(req_len, M_DEVBUF, M_NOWAIT | M_ZERO); + if (req == NULL) + return ENOMEM; + + hcmd.len[0] = (uint16_t)req_len; + hcmd.data[0] = (void *)req; + + /* These timings correspond to iwlwifi's UNASSOC scan. */ + req->active_dwell = 10; + req->passive_dwell = 110; + req->fragmented_dwell = 44; + req->extended_dwell = 90; + req->max_out_time = 0; + req->suspend_time = 0; + + req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH); + req->rx_chain_select = iwm_mvm_scan_rx_chain(sc); + req->iter_num = htole32(1); + req->delay = 0; + + req->scan_flags = htole32(IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL | + IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE | + IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL); + if (ssid_len == 0) + req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAG_PASSIVE); + else + req->scan_flags |= + htole32(IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION); + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) + req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED); + + req->flags = htole32(IWM_PHY_BAND_24); + if (sc->sc_nvm.sku_cap_band_52GHz_enable) + req->flags |= htole32(IWM_PHY_BAND_5); + req->filter_flags = + htole32(IWM_MAC_FILTER_ACCEPT_GRP | IWM_MAC_FILTER_IN_BEACON); + + /* Tx flags 2 GHz. */ + req->tx_cmd[0].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | IWM_TX_CMD_FLG_BT_DIS); - cmd->tx_cmd.sta_id = sc->sc_aux_sta.sta_id; - cmd->tx_cmd.life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); - cmd->tx_cmd.rate_n_flags = iwm_mvm_scan_rate_n_flags(sc, flags, 1/*XXX*/); + req->tx_cmd[0].rate_n_flags = + iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_2GHZ, 1/*XXX*/); + req->tx_cmd[0].sta_id = sc->sc_aux_sta.sta_id; - cmd->tx_cmd.len = htole16(iwm_mvm_fill_probe_req(sc, - (struct ieee80211_frame *)cmd->data, - vap ? vap->iv_myaddr : ic->ic_macaddr, n_ssids, - ssid, ssid_len, NULL, 0, - sc->sc_capa_max_probe_len)); + /* Tx flags 5 GHz. */ + req->tx_cmd[1].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | + IWM_TX_CMD_FLG_BT_DIS); + req->tx_cmd[1].rate_n_flags = + iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_5GHZ, 1/*XXX*/); + req->tx_cmd[1].sta_id = sc->sc_aux_sta.sta_id; - cmd->channel_count - = iwm_mvm_scan_fill_channels(sc, cmd, flags, n_ssids, basic_ssid); + /* Check if we're doing an active directed scan. */ + if (ssid_len != 0) { + req->direct_scan[0].id = IEEE80211_ELEMID_SSID; + req->direct_scan[0].len = ssid_len; + memcpy(req->direct_scan[0].ssid, ssid, ssid_len); + } - cmd->len = htole16(sizeof(struct iwm_scan_cmd) + - le16toh(cmd->tx_cmd.len) + - (cmd->channel_count * sizeof(struct iwm_scan_channel))); - hcmd.len[0] = le16toh(cmd->len); + req->n_channels = iwm_mvm_lmac_scan_fill_channels(sc, + (struct iwm_scan_channel_cfg_lmac *)req->data, + ssid_len != 0); - status = IWM_SCAN_RESPONSE_OK; - ret = iwm_mvm_send_cmd_status(sc, &hcmd, &status); - if (!ret && status == IWM_SCAN_RESPONSE_OK) { + ret = iwm_mvm_fill_probe_req(sc, + (struct iwm_scan_probe_req *)(req->data + + (sizeof(struct iwm_scan_channel_cfg_lmac) * + sc->sc_capa_n_scan_channels))); + if (ret) { + free(req, M_DEVBUF); + return ret; + } + + /* Specify the scan plan: We'll do one iteration. */ + req->schedule[0].iterations = 1; + req->schedule[0].full_scan_mul = 1; + + /* Disable EBS. */ + req->channel_opt[0].non_ebs_ratio = 1; + req->channel_opt[1].non_ebs_ratio = 1; + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) { IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Scan request was sent successfully\n"); - } else { - /* - * If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - ret = EIO; } + free(req, M_DEVBUF); return ret; } Index: head/sys/dev/iwm/if_iwm_scan.h =================================================================== --- head/sys/dev/iwm/if_iwm_scan.h (revision 303326) +++ head/sys/dev/iwm/if_iwm_scan.h (revision 303327) @@ -1,113 +1,113 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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. */ #ifndef __IF_IWN_SCAN_H__ #define __IF_IWN_SCAN_H__ -extern int -iwm_mvm_scan_request(struct iwm_softc *sc, int flags, - int n_ssids, uint8_t *ssid, int ssid_len); +extern int iwm_mvm_lmac_scan(struct iwm_softc *sc); +extern int iwm_mvm_config_umac_scan(struct iwm_softc *); +extern int iwm_mvm_umac_scan(struct iwm_softc *); #endif /* __IF_IWN_SCAN_H__ */ Index: head/sys/dev/iwm/if_iwm_time_event.c =================================================================== --- head/sys/dev/iwm/if_iwm_time_event.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_time_event.c (revision 303327) @@ -1,274 +1,274 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * For the high priority TE use a time event type that has similar priority to * the FW's action scan priority. */ #define IWM_MVM_ROC_TE_TYPE_NORMAL IWM_TE_P2P_DEVICE_DISCOVERABLE #define IWM_MVM_ROC_TE_TYPE_MGMT_TX IWM_TE_P2P_CLIENT_ASSOC /* used to convert from time event API v2 to v1 */ #define IWM_TE_V2_DEP_POLICY_MSK (IWM_TE_V2_DEP_OTHER | IWM_TE_V2_DEP_TSF |\ IWM_TE_V2_EVENT_SOCIOPATHIC) static inline uint16_t iwm_te_v2_get_notify(uint16_t policy) { return le16toh(policy) & IWM_TE_V2_NOTIF_MSK; } static inline uint16_t iwm_te_v2_get_dep_policy(uint16_t policy) { return (le16toh(policy) & IWM_TE_V2_DEP_POLICY_MSK) >> IWM_TE_V2_PLACEMENT_POS; } static inline uint16_t iwm_te_v2_get_absence(uint16_t policy) { return (le16toh(policy) & IWM_TE_V2_ABSENCE) >> IWM_TE_V2_ABSENCE_POS; } static void iwm_mvm_te_v2_to_v1(const struct iwm_time_event_cmd_v2 *cmd_v2, struct iwm_time_event_cmd_v1 *cmd_v1) { cmd_v1->id_and_color = cmd_v2->id_and_color; cmd_v1->action = cmd_v2->action; cmd_v1->id = cmd_v2->id; cmd_v1->apply_time = cmd_v2->apply_time; cmd_v1->max_delay = cmd_v2->max_delay; cmd_v1->depends_on = cmd_v2->depends_on; cmd_v1->interval = cmd_v2->interval; cmd_v1->duration = cmd_v2->duration; if (cmd_v2->repeat == IWM_TE_V2_REPEAT_ENDLESS) cmd_v1->repeat = htole32(IWM_TE_V1_REPEAT_ENDLESS); else cmd_v1->repeat = htole32(cmd_v2->repeat); cmd_v1->max_frags = htole32(cmd_v2->max_frags); cmd_v1->interval_reciprocal = 0; /* unused */ cmd_v1->dep_policy = htole32(iwm_te_v2_get_dep_policy(cmd_v2->policy)); cmd_v1->is_present = htole32(!iwm_te_v2_get_absence(cmd_v2->policy)); cmd_v1->notify = htole32(iwm_te_v2_get_notify(cmd_v2->policy)); } static int iwm_mvm_send_time_event_cmd(struct iwm_softc *sc, const struct iwm_time_event_cmd_v2 *cmd) { struct iwm_time_event_cmd_v1 cmd_v1; if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_TIME_EVENT_API_V2) return iwm_mvm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, IWM_CMD_SYNC, sizeof(*cmd), cmd); iwm_mvm_te_v2_to_v1(cmd, &cmd_v1); return iwm_mvm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, IWM_CMD_SYNC, sizeof(cmd_v1), &cmd_v1); } static int iwm_mvm_time_event_send_add(struct iwm_softc *sc, struct iwm_node *in, void *te_data, struct iwm_time_event_cmd_v2 *te_cmd) { int ret; IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "Add new TE, duration %d TU\n", le32toh(te_cmd->duration)); ret = iwm_mvm_send_time_event_cmd(sc, te_cmd); if (ret) { IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, "%s: Couldn't send IWM_TIME_EVENT_CMD: %d\n", __func__, ret); } return ret; } void iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_node *in, uint32_t duration, uint32_t max_delay) { struct iwm_time_event_cmd_v2 time_cmd; memset(&time_cmd, 0, sizeof(time_cmd)); time_cmd.action = htole32(IWM_FW_CTXT_ACTION_ADD); time_cmd.id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, IWM_DEFAULT_COLOR)); time_cmd.id = htole32(IWM_TE_BSS_STA_AGGRESSIVE_ASSOC); - time_cmd.apply_time = htole32(iwm_read_prph(sc, - IWM_DEVICE_SYSTEM_TIME_REG)); + time_cmd.apply_time = htole32(0); time_cmd.max_frags = IWM_TE_V2_FRAG_NONE; time_cmd.max_delay = htole32(max_delay); /* TODO: why do we need to interval = bi if it is not periodic? */ time_cmd.interval = htole32(1); time_cmd.duration = htole32(duration); time_cmd.repeat = 1; time_cmd.policy = htole32(IWM_TE_V2_NOTIF_HOST_EVENT_START | - IWM_TE_V2_NOTIF_HOST_EVENT_END); + IWM_TE_V2_NOTIF_HOST_EVENT_END | + IWM_T2_V2_START_IMMEDIATELY); iwm_mvm_time_event_send_add(sc, in, /*te_data*/NULL, &time_cmd); } Index: head/sys/dev/iwm/if_iwm_util.c =================================================================== --- head/sys/dev/iwm/if_iwm_util.c (revision 303326) +++ head/sys/dev/iwm/if_iwm_util.c (revision 303327) @@ -1,406 +1,458 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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 __FBSDID("$FreeBSD$"); #include "opt_wlan.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * Send a command to the firmware. We try to implement the Linux * driver interface for the routine. * mostly from if_iwn (iwn_cmd()). * * For now, we always copy the first part and map the second one (if it exists). */ int iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) { struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; struct iwm_tfd *desc; - struct iwm_tx_data *data; + struct iwm_tx_data *txdata = NULL; struct iwm_device_cmd *cmd; struct mbuf *m; bus_dma_segment_t seg; bus_addr_t paddr; uint32_t addr_lo; int error = 0, i, paylen, off; int code; int async, wantresp; + int group_id; int nsegs; + size_t hdrlen, datasz; + uint8_t *data; code = hcmd->id; async = hcmd->flags & IWM_CMD_ASYNC; wantresp = hcmd->flags & IWM_CMD_WANT_SKB; + data = NULL; for (i = 0, paylen = 0; i < nitems(hcmd->len); i++) { paylen += hcmd->len[i]; } /* if the command wants an answer, busy sc_cmd_resp */ if (wantresp) { KASSERT(!async, ("invalid async parameter")); while (sc->sc_wantresp != -1) msleep(&sc->sc_wantresp, &sc->sc_mtx, 0, "iwmcmdsl", 0); sc->sc_wantresp = ring->qid << 16 | ring->cur; IWM_DPRINTF(sc, IWM_DEBUG_CMD, "wantresp is %x\n", sc->sc_wantresp); } /* * Is the hardware still available? (after e.g. above wait). */ if (sc->sc_flags & IWM_FLAG_STOPPED) { error = ENXIO; goto out; } desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + txdata = &ring->data[ring->cur]; - if (paylen > sizeof(cmd->data)) { + group_id = iwm_cmd_groupid(code); + if (group_id != 0) { + hdrlen = sizeof(cmd->hdr_wide); + datasz = sizeof(cmd->data_wide); + } else { + hdrlen = sizeof(cmd->hdr); + datasz = sizeof(cmd->data); + } + + if (paylen > datasz) { IWM_DPRINTF(sc, IWM_DEBUG_CMD, "large command paylen=%u len0=%u\n", paylen, hcmd->len[0]); /* Command is too large */ + size_t totlen = hdrlen + paylen; if (paylen > IWM_MAX_CMD_PAYLOAD_SIZE) { device_printf(sc->sc_dev, "firmware command too long (%zd bytes)\n", - paylen + sizeof(cmd->hdr)); + totlen); error = EINVAL; goto out; } m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); if (m == NULL) { error = ENOBUFS; goto out; } m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; error = bus_dmamap_load_mbuf_sg(ring->data_dmat, - data->map, m, &seg, &nsegs, BUS_DMA_NOWAIT); + txdata->map, m, &seg, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, "%s: can't map mbuf, error %d\n", __func__, error); m_freem(m); goto out; } - data->m = m; /* mbuf will be freed in iwm_cmd_done() */ + txdata->m = m; /* mbuf will be freed in iwm_cmd_done() */ cmd = mtod(m, struct iwm_device_cmd *); paddr = seg.ds_addr; } else { cmd = &ring->cmd[ring->cur]; - paddr = data->cmd_paddr; + paddr = txdata->cmd_paddr; } - cmd->hdr.code = code; - cmd->hdr.flags = 0; - cmd->hdr.qid = ring->qid; - cmd->hdr.idx = ring->cur; + if (group_id != 0) { + cmd->hdr_wide.opcode = iwm_cmd_opcode(code); + cmd->hdr_wide.group_id = group_id; + cmd->hdr_wide.qid = ring->qid; + cmd->hdr_wide.idx = ring->cur; + cmd->hdr_wide.length = htole16(paylen); + cmd->hdr_wide.version = iwm_cmd_version(code); + data = cmd->data_wide; + } else { + cmd->hdr.code = iwm_cmd_opcode(code); + cmd->hdr.flags = 0; + cmd->hdr.qid = ring->qid; + cmd->hdr.idx = ring->cur; + data = cmd->data; + } for (i = 0, off = 0; i < nitems(hcmd->data); i++) { if (hcmd->len[i] == 0) continue; - memcpy(cmd->data + off, hcmd->data[i], hcmd->len[i]); + memcpy(data + off, hcmd->data[i], hcmd->len[i]); off += hcmd->len[i]; } KASSERT(off == paylen, ("off %d != paylen %d", off, paylen)); /* lo field is not aligned */ addr_lo = htole32((uint32_t)paddr); memcpy(&desc->tbs[0].lo, &addr_lo, sizeof(uint32_t)); desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(paddr) - | ((sizeof(cmd->hdr) + paylen) << 4)); + | ((hdrlen + paylen) << 4)); desc->num_tbs = 1; IWM_DPRINTF(sc, IWM_DEBUG_CMD, - "%s: iwm_send_cmd 0x%x size=%lu %s\n", - __func__, + "iwm_send_cmd 0x%x size=%lu %s\n", code, - (unsigned long) (hcmd->len[0] + hcmd->len[1] + sizeof(cmd->hdr)), + (unsigned long) (hcmd->len[0] + hcmd->len[1] + hdrlen), async ? " (async)" : ""); - if (paylen > sizeof(cmd->data)) { - bus_dmamap_sync(ring->data_dmat, data->map, + if (paylen > datasz) { + bus_dmamap_sync(ring->data_dmat, txdata->map, BUS_DMASYNC_PREWRITE); } else { bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, BUS_DMASYNC_PREWRITE); } bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, BUS_DMASYNC_PREWRITE); IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); if (!iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN, (IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY | IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP), 15000)) { device_printf(sc->sc_dev, "%s: acquiring device failed\n", __func__); error = EBUSY; goto out; } #if 0 iwm_update_sched(sc, ring->qid, ring->cur, 0, 0); #endif IWM_DPRINTF(sc, IWM_DEBUG_CMD, "sending command 0x%x qid %d, idx %d\n", code, ring->qid, ring->cur); /* Kick command ring. */ ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); if (!async) { /* m..m-mmyy-mmyyyy-mym-ym m-my generation */ int generation = sc->sc_generation; error = msleep(desc, &sc->sc_mtx, PCATCH, "iwmcmd", hz); if (error == 0) { /* if hardware is no longer up, return error */ if (generation != sc->sc_generation) { error = ENXIO; } else { hcmd->resp_pkt = (void *)sc->sc_cmd_resp; } } } out: if (wantresp && error != 0) { iwm_free_resp(sc, hcmd); } return error; } /* iwlwifi: mvm/utils.c */ int iwm_mvm_send_cmd_pdu(struct iwm_softc *sc, uint8_t id, uint32_t flags, uint16_t len, const void *data) { struct iwm_host_cmd cmd = { .id = id, .len = { len, }, .data = { data, }, .flags = flags, }; return iwm_send_cmd(sc, &cmd); } /* iwlwifi: mvm/utils.c */ int iwm_mvm_send_cmd_status(struct iwm_softc *sc, struct iwm_host_cmd *cmd, uint32_t *status) { struct iwm_rx_packet *pkt; struct iwm_cmd_response *resp; int error, resp_len; KASSERT((cmd->flags & IWM_CMD_WANT_SKB) == 0, ("invalid command")); cmd->flags |= IWM_CMD_SYNC | IWM_CMD_WANT_SKB; if ((error = iwm_send_cmd(sc, cmd)) != 0) return error; pkt = cmd->resp_pkt; /* Can happen if RFKILL is asserted */ if (!pkt) { error = 0; goto out_free_resp; } if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { error = EIO; goto out_free_resp; } resp_len = iwm_rx_packet_payload_len(pkt); if (resp_len != sizeof(*resp)) { error = EIO; goto out_free_resp; } resp = (void *)pkt->data; *status = le32toh(resp->status); out_free_resp: iwm_free_resp(sc, cmd); return error; } /* iwlwifi/mvm/utils.c */ int iwm_mvm_send_cmd_pdu_status(struct iwm_softc *sc, uint8_t id, uint16_t len, const void *data, uint32_t *status) { struct iwm_host_cmd cmd = { .id = id, .len = { len, }, .data = { data, }, }; return iwm_mvm_send_cmd_status(sc, &cmd, status); } void iwm_free_resp(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) { KASSERT(sc->sc_wantresp != -1, ("already freed")); KASSERT((hcmd->flags & (IWM_CMD_WANT_SKB|IWM_CMD_SYNC)) == (IWM_CMD_WANT_SKB|IWM_CMD_SYNC), ("invalid flags")); sc->sc_wantresp = -1; wakeup(&sc->sc_wantresp); +} + +uint8_t +iwm_fw_valid_tx_ant(struct iwm_softc *sc) +{ + uint8_t tx_ant; + + tx_ant = ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) + >> IWM_FW_PHY_CFG_TX_CHAIN_POS); + + if (sc->sc_nvm.valid_tx_ant) + tx_ant &= sc->sc_nvm.valid_tx_ant; + + return tx_ant; +} + +uint8_t +iwm_fw_valid_rx_ant(struct iwm_softc *sc) +{ + uint8_t rx_ant; + + rx_ant = ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) + >> IWM_FW_PHY_CFG_RX_CHAIN_POS); + + if (sc->sc_nvm.valid_rx_ant) + rx_ant &= sc->sc_nvm.valid_rx_ant; + + return rx_ant; } Index: head/sys/dev/iwm/if_iwm_util.h =================================================================== --- head/sys/dev/iwm/if_iwm_util.h (revision 303326) +++ head/sys/dev/iwm/if_iwm_util.h (revision 303327) @@ -1,119 +1,122 @@ /* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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. */ #ifndef __IF_IWM_UTIL_H__ #define __IF_IWM_UTIL_H__ extern int iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd); extern int iwm_mvm_send_cmd_pdu(struct iwm_softc *sc, uint8_t id, uint32_t flags, uint16_t len, const void *data); extern int iwm_mvm_send_cmd_status(struct iwm_softc *sc, struct iwm_host_cmd *cmd, uint32_t *status); extern int iwm_mvm_send_cmd_pdu_status(struct iwm_softc *sc, uint8_t id, uint16_t len, const void *data, uint32_t *status); extern void iwm_free_resp(struct iwm_softc *sc, struct iwm_host_cmd *hcmd); +extern uint8_t iwm_fw_valid_tx_ant(struct iwm_softc *sc); +extern uint8_t iwm_fw_valid_rx_ant(struct iwm_softc *sc); + #endif /* __IF_IWM_UTIL_H__ */ Index: head/sys/dev/iwm/if_iwmreg.h =================================================================== --- head/sys/dev/iwm/if_iwmreg.h (revision 303326) +++ head/sys/dev/iwm/if_iwmreg.h (revision 303327) @@ -1,5367 +1,6430 @@ -/* $OpenBSD: if_iwmreg.h,v 1.3 2015/02/23 10:25:20 stsp Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.4 2015/06/15 08:06:11 stsp Exp $ */ /* $FreeBSD$ */ /****************************************************************************** * * 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) 2005 - 2014 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 * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 * * BSD LICENSE * * Copyright(c) 2005 - 2014 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. * *****************************************************************************/ #ifndef __IF_IWM_REG_H__ #define __IF_IWM_REG_H__ #define le16_to_cpup(_a_) (le16toh(*(const uint16_t *)(_a_))) #define le32_to_cpup(_a_) (le32toh(*(const uint32_t *)(_a_))) /* * BEGIN iwl-csr.h */ /* * CSR (control and status registers) * * CSR registers are mapped directly into PCI bus space, and are accessible * whenever platform supplies power to device, even when device is in * low power states due to driver-invoked device resets * (e.g. IWM_CSR_RESET_REG_FLAG_SW_RESET) or uCode-driven power-saving modes. * * Use iwl_write32() and iwl_read32() family to access these registers; * these provide simple PCI bus access, without waking up the MAC. * Do not use iwl_write_direct32() family for these registers; * no need to "grab nic access" via IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ. * The MAC (uCode processor, etc.) does not need to be powered up for accessing * the CSR registers. * * NOTE: Device does need to be awake in order to read this memory * via IWM_CSR_EEPROM and IWM_CSR_OTP registers */ #define IWM_CSR_HW_IF_CONFIG_REG (0x000) /* hardware interface config */ #define IWM_CSR_INT_COALESCING (0x004) /* accum ints, 32-usec units */ #define IWM_CSR_INT (0x008) /* host interrupt status/ack */ #define IWM_CSR_INT_MASK (0x00c) /* host interrupt enable */ #define IWM_CSR_FH_INT_STATUS (0x010) /* busmaster int status/ack*/ #define IWM_CSR_GPIO_IN (0x018) /* read external chip pins */ #define IWM_CSR_RESET (0x020) /* busmaster enable, NMI, etc*/ #define IWM_CSR_GP_CNTRL (0x024) /* 2nd byte of IWM_CSR_INT_COALESCING, not accessible via iwl_write32()! */ #define IWM_CSR_INT_PERIODIC_REG (0x005) /* * Hardware revision info * Bit fields: * 31-16: Reserved * 15-4: Type of device: see IWM_CSR_HW_REV_TYPE_xxx definitions * 3-2: Revision step: 0 = A, 1 = B, 2 = C, 3 = D * 1-0: "Dash" (-) value, as in A-1, etc. */ #define IWM_CSR_HW_REV (0x028) /* * EEPROM and OTP (one-time-programmable) memory reads * * NOTE: Device must be awake, initialized via apm_ops.init(), * in order to read. */ #define IWM_CSR_EEPROM_REG (0x02c) #define IWM_CSR_EEPROM_GP (0x030) #define IWM_CSR_OTP_GP_REG (0x034) #define IWM_CSR_GIO_REG (0x03C) #define IWM_CSR_GP_UCODE_REG (0x048) #define IWM_CSR_GP_DRIVER_REG (0x050) /* * UCODE-DRIVER GP (general purpose) mailbox registers. * SET/CLR registers set/clear bit(s) if "1" is written. */ #define IWM_CSR_UCODE_DRV_GP1 (0x054) #define IWM_CSR_UCODE_DRV_GP1_SET (0x058) #define IWM_CSR_UCODE_DRV_GP1_CLR (0x05c) #define IWM_CSR_UCODE_DRV_GP2 (0x060) +#define IWM_CSR_MBOX_SET_REG (0x088) +#define IWM_CSR_MBOX_SET_REG_OS_ALIVE 0x20 + #define IWM_CSR_LED_REG (0x094) #define IWM_CSR_DRAM_INT_TBL_REG (0x0A0) #define IWM_CSR_MAC_SHADOW_REG_CTRL (0x0A8) /* 6000 and up */ /* GIO Chicken Bits (PCI Express bus link power management) */ #define IWM_CSR_GIO_CHICKEN_BITS (0x100) /* Analog phase-lock-loop configuration */ #define IWM_CSR_ANA_PLL_CFG (0x20c) /* * CSR Hardware Revision Workaround Register. Indicates hardware rev; * "step" determines CCK backoff for txpower calculation. Used for 4965 only. * See also IWM_CSR_HW_REV register. * Bit fields: * 3-2: 0 = A, 1 = B, 2 = C, 3 = D step * 1-0: "Dash" (-) value, as in C-1, etc. */ #define IWM_CSR_HW_REV_WA_REG (0x22C) #define IWM_CSR_DBG_HPET_MEM_REG (0x240) #define IWM_CSR_DBG_LINK_PWR_MGMT_REG (0x250) /* Bits for IWM_CSR_HW_IF_CONFIG_REG */ #define IWM_CSR_HW_IF_CONFIG_REG_MSK_MAC_DASH (0x00000003) #define IWM_CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP (0x0000000C) #define IWM_CSR_HW_IF_CONFIG_REG_MSK_BOARD_VER (0x000000C0) #define IWM_CSR_HW_IF_CONFIG_REG_BIT_MAC_SI (0x00000100) #define IWM_CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI (0x00000200) #define IWM_CSR_HW_IF_CONFIG_REG_MSK_PHY_TYPE (0x00000C00) #define IWM_CSR_HW_IF_CONFIG_REG_MSK_PHY_DASH (0x00003000) #define IWM_CSR_HW_IF_CONFIG_REG_MSK_PHY_STEP (0x0000C000) #define IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH (0) #define IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP (2) #define IWM_CSR_HW_IF_CONFIG_REG_POS_BOARD_VER (6) #define IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE (10) #define IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH (12) #define IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP (14) #define IWM_CSR_HW_IF_CONFIG_REG_BIT_HAP_WAKE_L1A (0x00080000) #define IWM_CSR_HW_IF_CONFIG_REG_BIT_EEPROM_OWN_SEM (0x00200000) #define IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define IWM_CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define IWM_CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) +#define IWM_CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ #define IWM_CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ #define IWM_CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ /* interrupt flags in INTA, set by uCode or hardware (e.g. dma), * acknowledged (reset) by host writing "1" to flagged bits. */ #define IWM_CSR_INT_BIT_FH_RX (1 << 31) /* Rx DMA, cmd responses, FH_INT[17:16] */ #define IWM_CSR_INT_BIT_HW_ERR (1 << 29) /* DMA hardware error FH_INT[31] */ #define IWM_CSR_INT_BIT_RX_PERIODIC (1 << 28) /* Rx periodic */ #define IWM_CSR_INT_BIT_FH_TX (1 << 27) /* Tx DMA FH_INT[1:0] */ #define IWM_CSR_INT_BIT_SCD (1 << 26) /* TXQ pointer advanced */ #define IWM_CSR_INT_BIT_SW_ERR (1 << 25) /* uCode error */ #define IWM_CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ #define IWM_CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ #define IWM_CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */ #define IWM_CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ #define IWM_CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ #define IWM_CSR_INI_SET_MASK (IWM_CSR_INT_BIT_FH_RX | \ IWM_CSR_INT_BIT_HW_ERR | \ IWM_CSR_INT_BIT_FH_TX | \ IWM_CSR_INT_BIT_SW_ERR | \ IWM_CSR_INT_BIT_RF_KILL | \ IWM_CSR_INT_BIT_SW_RX | \ IWM_CSR_INT_BIT_WAKEUP | \ IWM_CSR_INT_BIT_ALIVE | \ IWM_CSR_INT_BIT_RX_PERIODIC) /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ #define IWM_CSR_FH_INT_BIT_ERR (1 << 31) /* Error */ #define IWM_CSR_FH_INT_BIT_HI_PRIOR (1 << 30) /* High priority Rx, bypass coalescing */ #define IWM_CSR_FH_INT_BIT_RX_CHNL1 (1 << 17) /* Rx channel 1 */ #define IWM_CSR_FH_INT_BIT_RX_CHNL0 (1 << 16) /* Rx channel 0 */ #define IWM_CSR_FH_INT_BIT_TX_CHNL1 (1 << 1) /* Tx channel 1 */ #define IWM_CSR_FH_INT_BIT_TX_CHNL0 (1 << 0) /* Tx channel 0 */ #define IWM_CSR_FH_INT_RX_MASK (IWM_CSR_FH_INT_BIT_HI_PRIOR | \ IWM_CSR_FH_INT_BIT_RX_CHNL1 | \ IWM_CSR_FH_INT_BIT_RX_CHNL0) #define IWM_CSR_FH_INT_TX_MASK (IWM_CSR_FH_INT_BIT_TX_CHNL1 | \ IWM_CSR_FH_INT_BIT_TX_CHNL0) /* GPIO */ #define IWM_CSR_GPIO_IN_BIT_AUX_POWER (0x00000200) #define IWM_CSR_GPIO_IN_VAL_VAUX_PWR_SRC (0x00000000) #define IWM_CSR_GPIO_IN_VAL_VMAIN_PWR_SRC (0x00000200) /* RESET */ #define IWM_CSR_RESET_REG_FLAG_NEVO_RESET (0x00000001) #define IWM_CSR_RESET_REG_FLAG_FORCE_NMI (0x00000002) #define IWM_CSR_RESET_REG_FLAG_SW_RESET (0x00000080) #define IWM_CSR_RESET_REG_FLAG_MASTER_DISABLED (0x00000100) #define IWM_CSR_RESET_REG_FLAG_STOP_MASTER (0x00000200) #define IWM_CSR_RESET_LINK_PWR_MGMT_DISABLED (0x80000000) /* * GP (general purpose) CONTROL REGISTER * Bit fields: * 27: HW_RF_KILL_SW * Indicates state of (platform's) hardware RF-Kill switch * 26-24: POWER_SAVE_TYPE * Indicates current power-saving mode: * 000 -- No power saving * 001 -- MAC power-down * 010 -- PHY (radio) power-down * 011 -- Error * 9-6: SYS_CONFIG * Indicates current system configuration, reflecting pins on chip * as forced high/low by device circuit board. * 4: GOING_TO_SLEEP * Indicates MAC is entering a power-saving sleep power-down. * Not a good time to access device-internal resources. * 3: MAC_ACCESS_REQ * Host sets this to request and maintain MAC wakeup, to allow host * access to device-internal resources. Host must wait for * MAC_CLOCK_READY (and !GOING_TO_SLEEP) before accessing non-CSR * device registers. * 2: INIT_DONE * Host sets this to put device into fully operational D0 power mode. * Host resets this after SW_RESET to put device into low power mode. * 0: MAC_CLOCK_READY * Indicates MAC (ucode processor, etc.) is powered up and can run. * Internal resources are accessible. * NOTE: This does not indicate that the processor is actually running. * NOTE: This does not indicate that device has completed * init or post-power-down restore of internal SRAM memory. * Use IWM_CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP as indication that * SRAM is restored and uCode is in normal operation mode. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. * NOTE: After device reset, this bit remains "0" until host sets * INIT_DONE */ #define IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY (0x00000001) #define IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE (0x00000004) #define IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ (0x00000008) #define IWM_CSR_GP_CNTRL_REG_FLAG_GOING_TO_SLEEP (0x00000010) #define IWM_CSR_GP_CNTRL_REG_VAL_MAC_ACCESS_EN (0x00000001) #define IWM_CSR_GP_CNTRL_REG_MSK_POWER_SAVE_TYPE (0x07000000) #define IWM_CSR_GP_CNTRL_REG_FLAG_MAC_POWER_SAVE (0x04000000) #define IWM_CSR_GP_CNTRL_REG_FLAG_HW_RF_KILL_SW (0x08000000) /* HW REV */ #define IWM_CSR_HW_REV_DASH(_val) (((_val) & 0x0000003) >> 0) #define IWM_CSR_HW_REV_STEP(_val) (((_val) & 0x000000C) >> 2) #define IWM_CSR_HW_REV_TYPE_MSK (0x000FFF0) #define IWM_CSR_HW_REV_TYPE_5300 (0x0000020) #define IWM_CSR_HW_REV_TYPE_5350 (0x0000030) #define IWM_CSR_HW_REV_TYPE_5100 (0x0000050) #define IWM_CSR_HW_REV_TYPE_5150 (0x0000040) #define IWM_CSR_HW_REV_TYPE_1000 (0x0000060) #define IWM_CSR_HW_REV_TYPE_6x00 (0x0000070) #define IWM_CSR_HW_REV_TYPE_6x50 (0x0000080) #define IWM_CSR_HW_REV_TYPE_6150 (0x0000084) #define IWM_CSR_HW_REV_TYPE_6x05 (0x00000B0) #define IWM_CSR_HW_REV_TYPE_6x30 IWM_CSR_HW_REV_TYPE_6x05 #define IWM_CSR_HW_REV_TYPE_6x35 IWM_CSR_HW_REV_TYPE_6x05 #define IWM_CSR_HW_REV_TYPE_2x30 (0x00000C0) #define IWM_CSR_HW_REV_TYPE_2x00 (0x0000100) #define IWM_CSR_HW_REV_TYPE_105 (0x0000110) #define IWM_CSR_HW_REV_TYPE_135 (0x0000120) +#define IWM_CSR_HW_REV_TYPE_7265D (0x0000210) #define IWM_CSR_HW_REV_TYPE_NONE (0x00001F0) /* EEPROM REG */ #define IWM_CSR_EEPROM_REG_READ_VALID_MSK (0x00000001) #define IWM_CSR_EEPROM_REG_BIT_CMD (0x00000002) #define IWM_CSR_EEPROM_REG_MSK_ADDR (0x0000FFFC) #define IWM_CSR_EEPROM_REG_MSK_DATA (0xFFFF0000) /* EEPROM GP */ #define IWM_CSR_EEPROM_GP_VALID_MSK (0x00000007) /* signature */ #define IWM_CSR_EEPROM_GP_IF_OWNER_MSK (0x00000180) #define IWM_CSR_EEPROM_GP_BAD_SIGNATURE_BOTH_EEP_AND_OTP (0x00000000) #define IWM_CSR_EEPROM_GP_BAD_SIG_EEP_GOOD_SIG_OTP (0x00000001) #define IWM_CSR_EEPROM_GP_GOOD_SIG_EEP_LESS_THAN_4K (0x00000002) #define IWM_CSR_EEPROM_GP_GOOD_SIG_EEP_MORE_THAN_4K (0x00000004) /* One-time-programmable memory general purpose reg */ #define IWM_CSR_OTP_GP_REG_DEVICE_SELECT (0x00010000) /* 0 - EEPROM, 1 - OTP */ #define IWM_CSR_OTP_GP_REG_OTP_ACCESS_MODE (0x00020000) /* 0 - absolute, 1 - relative */ #define IWM_CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK (0x00100000) /* bit 20 */ #define IWM_CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK (0x00200000) /* bit 21 */ /* GP REG */ #define IWM_CSR_GP_REG_POWER_SAVE_STATUS_MSK (0x03000000) /* bit 24/25 */ #define IWM_CSR_GP_REG_NO_POWER_SAVE (0x00000000) #define IWM_CSR_GP_REG_MAC_POWER_SAVE (0x01000000) #define IWM_CSR_GP_REG_PHY_POWER_SAVE (0x02000000) #define IWM_CSR_GP_REG_POWER_SAVE_ERROR (0x03000000) /* CSR GIO */ #define IWM_CSR_GIO_REG_VAL_L0S_ENABLED (0x00000002) /* * UCODE-DRIVER GP (general purpose) mailbox register 1 * Host driver and uCode write and/or read this register to communicate with * each other. * Bit fields: * 4: UCODE_DISABLE * Host sets this to request permanent halt of uCode, same as * sending CARD_STATE command with "halt" bit set. * 3: CT_KILL_EXIT * Host sets this to request exit from CT_KILL state, i.e. host thinks * device temperature is low enough to continue normal operation. * 2: CMD_BLOCKED * Host sets this during RF KILL power-down sequence (HW, SW, CT KILL) * to release uCode to clear all Tx and command queues, enter * unassociated mode, and power down. * NOTE: Some devices also use HBUS_TARG_MBX_C register for this bit. * 1: SW_BIT_RFKILL * Host sets this when issuing CARD_STATE command to request * device sleep. * 0: MAC_SLEEP * uCode sets this when preparing a power-saving power-down. * uCode resets this when power-up is complete and SRAM is sane. * NOTE: device saves internal SRAM data to host when powering down, * and must restore this data after powering back up. * MAC_SLEEP is the best indication that restore is complete. * Later devices (5xxx/6xxx/1xxx) use non-volatile SRAM, and * do not need to save/restore it. */ #define IWM_CSR_UCODE_DRV_GP1_BIT_MAC_SLEEP (0x00000001) #define IWM_CSR_UCODE_SW_BIT_RFKILL (0x00000002) #define IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED (0x00000004) #define IWM_CSR_UCODE_DRV_GP1_REG_BIT_CT_KILL_EXIT (0x00000008) #define IWM_CSR_UCODE_DRV_GP1_BIT_D3_CFG_COMPLETE (0x00000020) /* GP Driver */ #define IWM_CSR_GP_DRIVER_REG_BIT_RADIO_SKU_MSK (0x00000003) #define IWM_CSR_GP_DRIVER_REG_BIT_RADIO_SKU_3x3_HYB (0x00000000) #define IWM_CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_HYB (0x00000001) #define IWM_CSR_GP_DRIVER_REG_BIT_RADIO_SKU_2x2_IPA (0x00000002) #define IWM_CSR_GP_DRIVER_REG_BIT_CALIB_VERSION6 (0x00000004) #define IWM_CSR_GP_DRIVER_REG_BIT_6050_1x2 (0x00000008) #define IWM_CSR_GP_DRIVER_REG_BIT_RADIO_IQ_INVER (0x00000080) /* GIO Chicken Bits (PCI Express bus link power management) */ #define IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_L1A_NO_L0S_RX (0x00800000) #define IWM_CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER (0x20000000) /* LED */ #define IWM_CSR_LED_BSM_CTRL_MSK (0xFFFFFFDF) #define IWM_CSR_LED_REG_TURN_ON (0x60) #define IWM_CSR_LED_REG_TURN_OFF (0x20) /* ANA_PLL */ #define IWM_CSR50_ANA_PLL_CFG_VAL (0x00880300) /* HPET MEM debug */ #define IWM_CSR_DBG_HPET_MEM_REG_VAL (0xFFFF0000) /* DRAM INT TABLE */ #define IWM_CSR_DRAM_INT_TBL_ENABLE (1 << 31) +#define IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28) #define IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) /* SECURE boot registers */ #define IWM_CSR_SECURE_BOOT_CONFIG_ADDR (0x100) enum iwm_secure_boot_config_reg { IWM_CSR_SECURE_BOOT_CONFIG_INSPECTOR_BURNED_IN_OTP = 0x00000001, IWM_CSR_SECURE_BOOT_CONFIG_INSPECTOR_NOT_REQ = 0x00000002, }; #define IWM_CSR_SECURE_BOOT_CPU1_STATUS_ADDR (0x100) #define IWM_CSR_SECURE_BOOT_CPU2_STATUS_ADDR (0x100) enum iwm_secure_boot_status_reg { IWM_CSR_SECURE_BOOT_CPU_STATUS_VERF_STATUS = 0x00000003, IWM_CSR_SECURE_BOOT_CPU_STATUS_VERF_COMPLETED = 0x00000002, IWM_CSR_SECURE_BOOT_CPU_STATUS_VERF_SUCCESS = 0x00000004, IWM_CSR_SECURE_BOOT_CPU_STATUS_VERF_FAIL = 0x00000008, IWM_CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010, }; -#define IWM_CSR_UCODE_LOAD_STATUS_ADDR (0x100) +#define IWM_FH_UCODE_LOAD_STATUS 0x1af0 +#define IWM_CSR_UCODE_LOAD_STATUS_ADDR 0x1e70 enum iwm_secure_load_status_reg { - IWM_CSR_CPU_STATUS_LOADING_STARTED = 0x00000001, - IWM_CSR_CPU_STATUS_LOADING_COMPLETED = 0x00000002, - IWM_CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, - IWM_CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, + IWM_LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001, + IWM_LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003, + IWM_LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007, + IWM_LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, + IWM_LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, }; +#define IWM_FH_MEM_TB_MAX_LENGTH 0x20000 -#define IWM_CSR_SECURE_INSPECTOR_CODE_ADDR (0x100) -#define IWM_CSR_SECURE_INSPECTOR_DATA_ADDR (0x100) +#define IWM_LMPM_SECURE_INSPECTOR_CODE_ADDR 0x1e38 +#define IWM_LMPM_SECURE_INSPECTOR_DATA_ADDR 0x1e3c +#define IWM_LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR 0x1e78 +#define IWM_LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR 0x1e7c +#define IWM_LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE 0x400000 +#define IWM_LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE 0x402000 +#define IWM_LMPM_SECURE_CPU1_HDR_MEM_SPACE 0x420000 +#define IWM_LMPM_SECURE_CPU2_HDR_MEM_SPACE 0x420400 + #define IWM_CSR_SECURE_TIME_OUT (100) +/* extended range in FW SRAM */ +#define IWM_FW_MEM_EXTENDED_START 0x40000 +#define IWM_FW_MEM_EXTENDED_END 0x57FFF + +/* FW chicken bits */ +#define IWM_LMPM_CHICK 0xa01ff8 +#define IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE 0x01 + #define IWM_FH_TCSR_0_REG0 (0x1D00) /* * HBUS (Host-side Bus) * * HBUS registers are mapped directly into PCI bus space, but are used * to indirectly access device's internal memory or registers that * may be powered-down. * * Use iwl_write_direct32()/iwl_read_direct32() family for these registers; * host must "grab nic access" via CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ * to make sure the MAC (uCode processor, etc.) is powered up for accessing * internal resources. * * Do not use iwl_write32()/iwl_read32() family to access these registers; * these provide only simple PCI bus access, without waking up the MAC. */ #define IWM_HBUS_BASE (0x400) /* * Registers for accessing device's internal SRAM memory (e.g. SCD SRAM * structures, error log, event log, verifying uCode load). * First write to address register, then read from or write to data register * to complete the job. Once the address register is set up, accesses to * data registers auto-increment the address by one dword. * Bit usage for address registers (read or write): * 0-31: memory address within device */ #define IWM_HBUS_TARG_MEM_RADDR (IWM_HBUS_BASE+0x00c) #define IWM_HBUS_TARG_MEM_WADDR (IWM_HBUS_BASE+0x010) #define IWM_HBUS_TARG_MEM_WDAT (IWM_HBUS_BASE+0x018) #define IWM_HBUS_TARG_MEM_RDAT (IWM_HBUS_BASE+0x01c) /* Mailbox C, used as workaround alternative to CSR_UCODE_DRV_GP1 mailbox */ #define IWM_HBUS_TARG_MBX_C (IWM_HBUS_BASE+0x030) #define IWM_HBUS_TARG_MBX_C_REG_BIT_CMD_BLOCKED (0x00000004) /* * Registers for accessing device's internal peripheral registers * (e.g. SCD, BSM, etc.). First write to address register, * then read from or write to data register to complete the job. * Bit usage for address registers (read or write): * 0-15: register address (offset) within device * 24-25: (# bytes - 1) to read or write (e.g. 3 for dword) */ #define IWM_HBUS_TARG_PRPH_WADDR (IWM_HBUS_BASE+0x044) #define IWM_HBUS_TARG_PRPH_RADDR (IWM_HBUS_BASE+0x048) #define IWM_HBUS_TARG_PRPH_WDAT (IWM_HBUS_BASE+0x04c) #define IWM_HBUS_TARG_PRPH_RDAT (IWM_HBUS_BASE+0x050) +/* enable the ID buf for read */ +#define IWM_WFPM_PS_CTL_CLR 0xa0300c +#define IWM_WFMP_MAC_ADDR_0 0xa03080 +#define IWM_WFMP_MAC_ADDR_1 0xa03084 +#define IWM_LMPM_PMG_EN 0xa01cec +#define IWM_RADIO_REG_SYS_MANUAL_DFT_0 0xad4078 +#define IWM_RFIC_REG_RD 0xad0470 +#define IWM_WFPM_CTRL_REG 0xa03030 +#define IWM_WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK 0x08000000 +#define IWM_ENABLE_WFPM 0x80000000 + +#define IWM_AUX_MISC_REG 0xa200b0 +#define IWM_HW_STEP_LOCATION_BITS 24 + +#define IWM_AUX_MISC_MASTER1_EN 0xa20818 +#define IWM_AUX_MISC_MASTER1_EN_SBE_MSK 0x1 +#define IWM_AUX_MISC_MASTER1_SMPHR_STATUS 0xa20800 +#define IWM_RSA_ENABLE 0xa24b08 +#define IWM_PREG_AUX_BUS_WPROT_0 0xa04cc0 +#define IWM_SB_CFG_OVERRIDE_ADDR 0xa26c78 +#define IWM_SB_CFG_OVERRIDE_ENABLE 0x8000 +#define IWM_SB_CFG_BASE_OVERRIDE 0xa20000 +#define IWM_SB_MODIFY_CFG_FLAG 0xa03088 +#define IWM_SB_CPU_1_STATUS 0xa01e30 +#define IWM_SB_CPU_2_STATUS 0Xa01e34 + /* Used to enable DBGM */ #define IWM_HBUS_TARG_TEST_REG (IWM_HBUS_BASE+0x05c) /* * Per-Tx-queue write pointer (index, really!) * Indicates index to next TFD that driver will fill (1 past latest filled). * Bit usage: * 0-7: queue write index * 11-8: queue selector */ #define IWM_HBUS_TARG_WRPTR (IWM_HBUS_BASE+0x060) /********************************************************** * CSR values **********************************************************/ /* * host interrupt timeout value * used with setting interrupt coalescing timer * the CSR_INT_COALESCING is an 8 bit register in 32-usec unit * * default interrupt coalescing timer is 64 x 32 = 2048 usecs */ #define IWM_HOST_INT_TIMEOUT_MAX (0xFF) #define IWM_HOST_INT_TIMEOUT_DEF (0x40) #define IWM_HOST_INT_TIMEOUT_MIN (0x0) #define IWM_HOST_INT_OPER_MODE (1 << 31) /***************************************************************************** * 7000/3000 series SHR DTS addresses * *****************************************************************************/ /* Diode Results Register Structure: */ enum iwm_dtd_diode_reg { IWM_DTS_DIODE_REG_DIG_VAL = 0x000000FF, /* bits [7:0] */ IWM_DTS_DIODE_REG_VREF_LOW = 0x0000FF00, /* bits [15:8] */ IWM_DTS_DIODE_REG_VREF_HIGH = 0x00FF0000, /* bits [23:16] */ IWM_DTS_DIODE_REG_VREF_ID = 0x03000000, /* bits [25:24] */ IWM_DTS_DIODE_REG_PASS_ONCE = 0x80000000, /* bits [31:31] */ IWM_DTS_DIODE_REG_FLAGS_MSK = 0xFF000000, /* bits [31:24] */ /* Those are the masks INSIDE the flags bit-field: */ IWM_DTS_DIODE_REG_FLAGS_VREFS_ID_POS = 0, IWM_DTS_DIODE_REG_FLAGS_VREFS_ID = 0x00000003, /* bits [1:0] */ IWM_DTS_DIODE_REG_FLAGS_PASS_ONCE_POS = 7, IWM_DTS_DIODE_REG_FLAGS_PASS_ONCE = 0x00000080, /* bits [7:7] */ }; /* * END iwl-csr.h */ /* * BEGIN iwl-fw.h */ /** * enum iwm_ucode_tlv_flag - ucode API flags * @IWM_UCODE_TLV_FLAGS_PAN: This is PAN capable microcode; this previously * was a separate TLV but moved here to save space. * @IWM_UCODE_TLV_FLAGS_NEWSCAN: new uCode scan behaviour on hidden SSID, * treats good CRC threshold as a boolean * @IWM_UCODE_TLV_FLAGS_MFP: This uCode image supports MFP (802.11w). * @IWM_UCODE_TLV_FLAGS_P2P: This uCode image supports P2P. * @IWM_UCODE_TLV_FLAGS_DW_BC_TABLE: The SCD byte count table is in DWORDS * @IWM_UCODE_TLV_FLAGS_UAPSD: This uCode image supports uAPSD * @IWM_UCODE_TLV_FLAGS_SHORT_BL: 16 entries of black list instead of 64 in scan * offload profile config command. * @IWM_UCODE_TLV_FLAGS_RX_ENERGY_API: supports rx signal strength api * @IWM_UCODE_TLV_FLAGS_TIME_EVENT_API_V2: using the new time event API. * @IWM_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS: D3 image supports up to six * (rather than two) IPv6 addresses * @IWM_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API * @IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element * from the probe request template. * @IWM_UCODE_TLV_FLAGS_D3_CONTINUITY_API: modified D3 API to allow keeping * connection when going back to D0 * @IWM_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) * @IWM_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) * @IWM_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. * @IWM_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API * @IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD: support device wide power command * containing CAM (Continuous Active Mode) indication. * @IWM_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a * single bound interface). + * @IWM_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD + * @IWM_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS. * @IWM_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save + * @IWM_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. + * @IWM_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients + * */ enum iwm_ucode_tlv_flag { IWM_UCODE_TLV_FLAGS_PAN = (1 << 0), IWM_UCODE_TLV_FLAGS_NEWSCAN = (1 << 1), IWM_UCODE_TLV_FLAGS_MFP = (1 << 2), IWM_UCODE_TLV_FLAGS_P2P = (1 << 3), IWM_UCODE_TLV_FLAGS_DW_BC_TABLE = (1 << 4), IWM_UCODE_TLV_FLAGS_NEWBT_COEX = (1 << 5), IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT = (1 << 6), IWM_UCODE_TLV_FLAGS_SHORT_BL = (1 << 7), IWM_UCODE_TLV_FLAGS_RX_ENERGY_API = (1 << 8), IWM_UCODE_TLV_FLAGS_TIME_EVENT_API_V2 = (1 << 9), IWM_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = (1 << 10), IWM_UCODE_TLV_FLAGS_BF_UPDATED = (1 << 11), IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID = (1 << 12), IWM_UCODE_TLV_FLAGS_D3_CONTINUITY_API = (1 << 14), IWM_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = (1 << 15), IWM_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = (1 << 16), IWM_UCODE_TLV_FLAGS_SCHED_SCAN = (1 << 17), IWM_UCODE_TLV_FLAGS_STA_KEY_CMD = (1 << 19), IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD = (1 << 20), IWM_UCODE_TLV_FLAGS_P2P_PS = (1 << 21), + IWM_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = (1 << 22), + IWM_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = (1 << 23), IWM_UCODE_TLV_FLAGS_UAPSD_SUPPORT = (1 << 24), + IWM_UCODE_TLV_FLAGS_EBS_SUPPORT = (1 << 25), IWM_UCODE_TLV_FLAGS_P2P_PS_UAPSD = (1 << 26), + IWM_UCODE_TLV_FLAGS_BCAST_FILTERING = (1 << 29), + IWM_UCODE_TLV_FLAGS_GO_UAPSD = (1 << 30), + IWM_UCODE_TLV_FLAGS_LTE_COEX = (1 << 31), }; +#define IWM_UCODE_TLV_FLAG_BITS \ + "\020\1PAN\2NEWSCAN\3MFP\4P2P\5DW_BC_TABLE\6NEWBT_COEX\7PM_CMD\10SHORT_BL\11RX_ENERG \ +Y\12TIME_EVENT_V2\13D3_6_IPV6\14BF_UPDATED\15NO_BASIC_SSID\17D3_CONTINUITY\20NEW_NSOFF \ +L_S\21NEW_NSOFFL_L\22SCHED_SCAN\24STA_KEY_CMD\25DEVICE_PS_CMD\26P2P_PS\27P2P_PS_DCM\30 \ +P2P_PS_SCM\31UAPSD_SUPPORT\32EBS\33P2P_PS_UAPSD\36BCAST_FILTERING\37GO_UAPSD\40LTE_COEX" + +/** + * enum iwm_ucode_tlv_api - ucode api + * @IWM_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time + * longer than the passive one, which is essential for fragmented scan. + * @IWM_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. + * @IWM_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header + * @IWM_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params + * @IWM_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority + * instead of 3. + * @IWM_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size + * (command version 3) that supports per-chain limits + * + * @IWM_NUM_UCODE_TLV_API: number of bits used + */ +enum iwm_ucode_tlv_api { + IWM_UCODE_TLV_API_FRAGMENTED_SCAN = (1 << 8), + IWM_UCODE_TLV_API_WIFI_MCC_UPDATE = (1 << 9), + IWM_UCODE_TLV_API_WIDE_CMD_HDR = (1 << 14), + IWM_UCODE_TLV_API_LQ_SS_PARAMS = (1 << 18), + IWM_UCODE_TLV_API_EXT_SCAN_PRIORITY = (1 << 24), + IWM_UCODE_TLV_API_TX_POWER_CHAIN = (1 << 27), + + IWM_NUM_UCODE_TLV_API = 32 +}; + +#define IWM_UCODE_TLV_API_BITS \ + "\020\10FRAGMENTED_SCAN\11WIFI_MCC_UPDATE\16WIDE_CMD_HDR\22LQ_SS_PARAMS\30EXT_SCAN_PRIO\33TX_POWER_CHAIN" + +/** + * enum iwm_ucode_tlv_capa - ucode capabilities + * @IWM_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 + * @IWM_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory + * @IWM_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan. + * @IWM_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer + * @IWM_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM) + * @IWM_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality + * @IWM_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current + * tx power value into TPC Report action frame and Link Measurement Report + * action frame + * @IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current + * channel in DS parameter set element in probe requests. + * @IWM_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in + * probe requests. + * @IWM_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests + * @IWM_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), + * which also implies support for the scheduler configuration command + * @IWM_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching + * @IWM_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG: Consolidated D3-D0 image + * @IWM_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command + * @IWM_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command + * @IWM_UCODE_TLV_CAPA_2G_COEX_SUPPORT: supports 2G coex Command + * @IWM_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload + * @IWM_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics + * @IWM_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD: support p2p standalone U-APSD + * @IWM_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running + * @IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different + * sources for the MCC. This TLV bit is a future replacement to + * IWM_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR + * is supported. + * @IWM_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC + * @IWM_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan + * @IWM_UCODE_TLV_CAPA_NAN_SUPPORT: supports NAN + * @IWM_UCODE_TLV_CAPA_UMAC_UPLOAD: supports upload mode in umac (1=supported, + * 0=no support) + * @IWM_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement + * @IWM_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts + * @IWM_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT + * @IWM_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what + * antenna the beacon should be transmitted + * @IWM_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon + * from AP and will send it upon d0i3 exit. + * @IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2 + * @IWM_UCODE_TLV_CAPA_CT_KILL_BY_FW: firmware responsible for CT-kill + * @IWM_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT: supports temperature + * thresholds reporting + * @IWM_UCODE_TLV_CAPA_CTDP_SUPPORT: supports cTDP command + * @IWM_UCODE_TLV_CAPA_USNIFFER_UNIFIED: supports usniffer enabled in + * regular image. + * @IWM_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG: support getting more shared + * memory addresses from the firmware. + * @IWM_UCODE_TLV_CAPA_LQM_SUPPORT: supports Link Quality Measurement + * @IWM_UCODE_TLV_CAPA_LMAC_UPLOAD: supports upload mode in lmac (1=supported, + * 0=no support) + * + * @IWM_NUM_UCODE_TLV_CAPA: number of bits used + */ +enum iwm_ucode_tlv_capa { + IWM_UCODE_TLV_CAPA_D0I3_SUPPORT = 0, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT = 1, + IWM_UCODE_TLV_CAPA_UMAC_SCAN = 2, + IWM_UCODE_TLV_CAPA_BEAMFORMER = 3, + IWM_UCODE_TLV_CAPA_TOF_SUPPORT = 5, + IWM_UCODE_TLV_CAPA_TDLS_SUPPORT = 6, + IWM_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = 8, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = 9, + IWM_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = 10, + IWM_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = 11, + IWM_UCODE_TLV_CAPA_DQA_SUPPORT = 12, + IWM_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = 13, + IWM_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG = 17, + IWM_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = 18, + IWM_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = 19, + IWM_UCODE_TLV_CAPA_2G_COEX_SUPPORT = 20, + IWM_UCODE_TLV_CAPA_CSUM_SUPPORT = 21, + IWM_UCODE_TLV_CAPA_RADIO_BEACON_STATS = 22, + IWM_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD = 26, + IWM_UCODE_TLV_CAPA_BT_COEX_PLCR = 28, + IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC = 29, + IWM_UCODE_TLV_CAPA_BT_COEX_RRC = 30, + IWM_UCODE_TLV_CAPA_GSCAN_SUPPORT = 31, + IWM_UCODE_TLV_CAPA_NAN_SUPPORT = 34, + IWM_UCODE_TLV_CAPA_UMAC_UPLOAD = 35, + IWM_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = 64, + IWM_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = 65, + IWM_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = 67, + IWM_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = 68, + IWM_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = 71, + IWM_UCODE_TLV_CAPA_BEACON_STORING = 72, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = 73, + IWM_UCODE_TLV_CAPA_CT_KILL_BY_FW = 74, + IWM_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT = 75, + IWM_UCODE_TLV_CAPA_CTDP_SUPPORT = 76, + IWM_UCODE_TLV_CAPA_USNIFFER_UNIFIED = 77, + IWM_UCODE_TLV_CAPA_LMAC_UPLOAD = 79, + IWM_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = 80, + IWM_UCODE_TLV_CAPA_LQM_SUPPORT = 81, + + IWM_NUM_UCODE_TLV_CAPA = 128 +}; + /* The default calibrate table size if not specified by firmware file */ #define IWM_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE 18 #define IWM_MAX_STANDARD_PHY_CALIBRATE_TBL_SIZE 19 #define IWM_MAX_PHY_CALIBRATE_TBL_SIZE 253 /* The default max probe length if not specified by the firmware file */ #define IWM_DEFAULT_MAX_PROBE_LENGTH 200 /* * enumeration of ucode section. * This enumeration is used directly for older firmware (before 16.0). * For new firmware, there can be up to 4 sections (see below) but the * first one packaged into the firmware file is the DATA section and * some debugging code accesses that. */ enum iwm_ucode_sec { IWM_UCODE_SECTION_DATA, IWM_UCODE_SECTION_INST, }; /* * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. */ -#define IWM_UCODE_SECTION_MAX 6 -#define IWM_UCODE_FIRST_SECTION_OF_SECOND_CPU (IWM_UCODE_SECTION_MAX/2) +#define IWM_CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC +#define IWM_PAGING_SEPARATOR_SECTION 0xAAAABBBB /* uCode version contains 4 values: Major/Minor/API/Serial */ #define IWM_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) #define IWM_UCODE_MINOR(ver) (((ver) & 0x00FF0000) >> 16) #define IWM_UCODE_API(ver) (((ver) & 0x0000FF00) >> 8) #define IWM_UCODE_SERIAL(ver) ((ver) & 0x000000FF) /* * Calibration control struct. * Sent as part of the phy configuration command. * @flow_trigger: bitmap for which calibrations to perform according to * flow triggers. * @event_trigger: bitmap for which calibrations to perform according to * event triggers. */ struct iwm_tlv_calib_ctrl { uint32_t flow_trigger; uint32_t event_trigger; } __packed; enum iwm_fw_phy_cfg { IWM_FW_PHY_CFG_RADIO_TYPE_POS = 0, IWM_FW_PHY_CFG_RADIO_TYPE = 0x3 << IWM_FW_PHY_CFG_RADIO_TYPE_POS, IWM_FW_PHY_CFG_RADIO_STEP_POS = 2, IWM_FW_PHY_CFG_RADIO_STEP = 0x3 << IWM_FW_PHY_CFG_RADIO_STEP_POS, IWM_FW_PHY_CFG_RADIO_DASH_POS = 4, IWM_FW_PHY_CFG_RADIO_DASH = 0x3 << IWM_FW_PHY_CFG_RADIO_DASH_POS, IWM_FW_PHY_CFG_TX_CHAIN_POS = 16, IWM_FW_PHY_CFG_TX_CHAIN = 0xf << IWM_FW_PHY_CFG_TX_CHAIN_POS, IWM_FW_PHY_CFG_RX_CHAIN_POS = 20, IWM_FW_PHY_CFG_RX_CHAIN = 0xf << IWM_FW_PHY_CFG_RX_CHAIN_POS, }; #define IWM_UCODE_MAX_CS 1 /** * struct iwm_fw_cipher_scheme - a cipher scheme supported by FW. * @cipher: a cipher suite selector * @flags: cipher scheme flags (currently reserved for a future use) * @hdr_len: a size of MPDU security header * @pn_len: a size of PN * @pn_off: an offset of pn from the beginning of the security header * @key_idx_off: an offset of key index byte in the security header * @key_idx_mask: a bit mask of key_idx bits * @key_idx_shift: bit shift needed to get key_idx * @mic_len: mic length in bytes * @hw_cipher: a HW cipher index used in host commands */ struct iwm_fw_cipher_scheme { uint32_t cipher; uint8_t flags; uint8_t hdr_len; uint8_t pn_len; uint8_t pn_off; uint8_t key_idx_off; uint8_t key_idx_mask; uint8_t key_idx_shift; uint8_t mic_len; uint8_t hw_cipher; } __packed; /** * struct iwm_fw_cscheme_list - a cipher scheme list * @size: a number of entries * @cs: cipher scheme entries */ struct iwm_fw_cscheme_list { uint8_t size; struct iwm_fw_cipher_scheme cs[]; } __packed; /* * END iwl-fw.h */ /* * BEGIN iwl-fw-file.h */ /* v1/v2 uCode file layout */ struct iwm_ucode_header { uint32_t ver; /* major/minor/API/serial */ union { struct { uint32_t inst_size; /* bytes of runtime code */ uint32_t data_size; /* bytes of runtime data */ uint32_t init_size; /* bytes of init code */ uint32_t init_data_size; /* bytes of init data */ uint32_t boot_size; /* bytes of bootstrap code */ uint8_t data[0]; /* in same order as sizes */ } v1; struct { uint32_t build; /* build number */ uint32_t inst_size; /* bytes of runtime code */ uint32_t data_size; /* bytes of runtime data */ uint32_t init_size; /* bytes of init code */ uint32_t init_data_size; /* bytes of init data */ uint32_t boot_size; /* bytes of bootstrap code */ uint8_t data[0]; /* in same order as sizes */ } v2; } u; }; /* * new TLV uCode file layout * * The new TLV file format contains TLVs, that each specify * some piece of data. */ enum iwm_ucode_tlv_type { IWM_UCODE_TLV_INVALID = 0, /* unused */ IWM_UCODE_TLV_INST = 1, IWM_UCODE_TLV_DATA = 2, IWM_UCODE_TLV_INIT = 3, IWM_UCODE_TLV_INIT_DATA = 4, IWM_UCODE_TLV_BOOT = 5, IWM_UCODE_TLV_PROBE_MAX_LEN = 6, /* a uint32_t value */ IWM_UCODE_TLV_PAN = 7, IWM_UCODE_TLV_RUNT_EVTLOG_PTR = 8, IWM_UCODE_TLV_RUNT_EVTLOG_SIZE = 9, IWM_UCODE_TLV_RUNT_ERRLOG_PTR = 10, IWM_UCODE_TLV_INIT_EVTLOG_PTR = 11, IWM_UCODE_TLV_INIT_EVTLOG_SIZE = 12, IWM_UCODE_TLV_INIT_ERRLOG_PTR = 13, IWM_UCODE_TLV_ENHANCE_SENS_TBL = 14, IWM_UCODE_TLV_PHY_CALIBRATION_SIZE = 15, IWM_UCODE_TLV_WOWLAN_INST = 16, IWM_UCODE_TLV_WOWLAN_DATA = 17, IWM_UCODE_TLV_FLAGS = 18, IWM_UCODE_TLV_SEC_RT = 19, IWM_UCODE_TLV_SEC_INIT = 20, IWM_UCODE_TLV_SEC_WOWLAN = 21, IWM_UCODE_TLV_DEF_CALIB = 22, IWM_UCODE_TLV_PHY_SKU = 23, IWM_UCODE_TLV_SECURE_SEC_RT = 24, IWM_UCODE_TLV_SECURE_SEC_INIT = 25, IWM_UCODE_TLV_SECURE_SEC_WOWLAN = 26, IWM_UCODE_TLV_NUM_OF_CPU = 27, IWM_UCODE_TLV_CSCHEME = 28, /* * Following two are not in our base tag, but allow * handling ucode version 9. */ IWM_UCODE_TLV_API_CHANGES_SET = 29, - IWM_UCODE_TLV_ENABLED_CAPABILITIES = 30 + IWM_UCODE_TLV_ENABLED_CAPABILITIES = 30, + + IWM_UCODE_TLV_N_SCAN_CHANNELS = 31, + IWM_UCODE_TLV_PAGING = 32, + IWM_UCODE_TLV_SEC_RT_USNIFFER = 34, + IWM_UCODE_TLV_SDIO_ADMA_ADDR = 35, + IWM_UCODE_TLV_FW_VERSION = 36, + IWM_UCODE_TLV_FW_DBG_DEST = 38, + IWM_UCODE_TLV_FW_DBG_CONF = 39, + IWM_UCODE_TLV_FW_DBG_TRIGGER = 40, + IWM_UCODE_TLV_FW_GSCAN_CAPA = 50, }; struct iwm_ucode_tlv { uint32_t type; /* see above */ uint32_t length; /* not including type/length fields */ uint8_t data[0]; }; +struct iwm_ucode_api { + uint32_t api_index; + uint32_t api_flags; +} __packed; + +struct iwm_ucode_capa { + uint32_t api_index; + uint32_t api_capa; +} __packed; + #define IWM_TLV_UCODE_MAGIC 0x0a4c5749 struct iwm_tlv_ucode_header { /* * The TLV style ucode header is distinguished from * the v1/v2 style header by first four bytes being * zero, as such is an invalid combination of * major/minor/API/serial versions. */ uint32_t zero; uint32_t magic; uint8_t human_readable[64]; uint32_t ver; /* major/minor/API/serial */ uint32_t build; uint64_t ignore; /* * The data contained herein has a TLV layout, * see above for the TLV header and types. * Note that each TLV is padded to a length * that is a multiple of 4 for alignment. */ uint8_t data[0]; }; /* * END iwl-fw-file.h */ /* * BEGIN iwl-prph.h */ /* * Registers in this file are internal, not PCI bus memory mapped. * Driver accesses these via IWM_HBUS_TARG_PRPH_* registers. */ #define IWM_PRPH_BASE (0x00000) #define IWM_PRPH_END (0xFFFFF) /* APMG (power management) constants */ #define IWM_APMG_BASE (IWM_PRPH_BASE + 0x3000) #define IWM_APMG_CLK_CTRL_REG (IWM_APMG_BASE + 0x0000) #define IWM_APMG_CLK_EN_REG (IWM_APMG_BASE + 0x0004) #define IWM_APMG_CLK_DIS_REG (IWM_APMG_BASE + 0x0008) #define IWM_APMG_PS_CTRL_REG (IWM_APMG_BASE + 0x000c) #define IWM_APMG_PCIDEV_STT_REG (IWM_APMG_BASE + 0x0010) #define IWM_APMG_RFKILL_REG (IWM_APMG_BASE + 0x0014) #define IWM_APMG_RTC_INT_STT_REG (IWM_APMG_BASE + 0x001c) #define IWM_APMG_RTC_INT_MSK_REG (IWM_APMG_BASE + 0x0020) #define IWM_APMG_DIGITAL_SVR_REG (IWM_APMG_BASE + 0x0058) #define IWM_APMG_ANALOG_SVR_REG (IWM_APMG_BASE + 0x006C) #define IWM_APMS_CLK_VAL_MRB_FUNC_MODE (0x00000001) #define IWM_APMG_CLK_VAL_DMA_CLK_RQT (0x00000200) #define IWM_APMG_CLK_VAL_BSM_CLK_RQT (0x00000800) #define IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS (0x00400000) #define IWM_APMG_PS_CTRL_VAL_RESET_REQ (0x04000000) #define IWM_APMG_PS_CTRL_MSK_PWR_SRC (0x03000000) #define IWM_APMG_PS_CTRL_VAL_PWR_SRC_VMAIN (0x00000000) #define IWM_APMG_PS_CTRL_VAL_PWR_SRC_VAUX (0x02000000) #define IWM_APMG_SVR_VOLTAGE_CONFIG_BIT_MSK (0x000001E0) /* bit 8:5 */ #define IWM_APMG_SVR_DIGITAL_VOLTAGE_1_32 (0x00000060) #define IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS (0x00000800) #define IWM_APMG_RTC_INT_STT_RFKILL (0x10000000) /* Device system time */ #define IWM_DEVICE_SYSTEM_TIME_REG 0xA0206C /* Device NMI register */ -#define IWM_DEVICE_SET_NMI_REG 0x00a01c30 +#define IWM_DEVICE_SET_NMI_REG 0x00a01c30 +#define IWM_DEVICE_SET_NMI_VAL_HW 0x01 +#define IWM_DEVICE_SET_NMI_VAL_DRV 0x80 +#define IWM_DEVICE_SET_NMI_8000_REG 0x00a01c24 +#define IWM_DEVICE_SET_NMI_8000_VAL 0x1000000 +/* + * Device reset for family 8000 + * write to bit 24 in order to reset the CPU + */ +#define IWM_RELEASE_CPU_RESET 0x300c +#define IWM_RELEASE_CPU_RESET_BIT 0x1000000 + + /***************************************************************************** * 7000/3000 series SHR DTS addresses * *****************************************************************************/ #define IWM_SHR_MISC_WFM_DTS_EN (0x00a10024) #define IWM_DTSC_CFG_MODE (0x00a10604) #define IWM_DTSC_VREF_AVG (0x00a10648) #define IWM_DTSC_VREF5_AVG (0x00a1064c) #define IWM_DTSC_CFG_MODE_PERIODIC (0x2) #define IWM_DTSC_PTAT_AVG (0x00a10650) /** * Tx Scheduler * * The Tx Scheduler selects the next frame to be transmitted, choosing TFDs * (Transmit Frame Descriptors) from up to 16 circular Tx queues resident in * host DRAM. It steers each frame's Tx command (which contains the frame * data) into one of up to 7 prioritized Tx DMA FIFO channels within the * device. A queue maps to only one (selectable by driver) Tx DMA channel, * but one DMA channel may take input from several queues. * * Tx DMA FIFOs have dedicated purposes. * * For 5000 series and up, they are used differently * (cf. iwl5000_default_queue_to_tx_fifo in iwl-5000.c): * * 0 -- EDCA BK (background) frames, lowest priority * 1 -- EDCA BE (best effort) frames, normal priority * 2 -- EDCA VI (video) frames, higher priority * 3 -- EDCA VO (voice) and management frames, highest priority * 4 -- unused * 5 -- unused * 6 -- unused * 7 -- Commands * * Driver should normally map queues 0-6 to Tx DMA/FIFO channels 0-6. * In addition, driver can map the remaining queues to Tx DMA/FIFO * channels 0-3 to support 11n aggregation via EDCA DMA channels. * * The driver sets up each queue to work in one of two modes: * * 1) Scheduler-Ack, in which the scheduler automatically supports a * block-ack (BA) window of up to 64 TFDs. In this mode, each queue * contains TFDs for a unique combination of Recipient Address (RA) * and Traffic Identifier (TID), that is, traffic of a given * Quality-Of-Service (QOS) priority, destined for a single station. * * In scheduler-ack mode, the scheduler keeps track of the Tx status of * each frame within the BA window, including whether it's been transmitted, * and whether it's been acknowledged by the receiving station. The device * automatically processes block-acks received from the receiving STA, * and reschedules un-acked frames to be retransmitted (successful * Tx completion may end up being out-of-order). * * The driver must maintain the queue's Byte Count table in host DRAM * for this mode. * This mode does not support fragmentation. * * 2) FIFO (a.k.a. non-Scheduler-ACK), in which each TFD is processed in order. * The device may automatically retry Tx, but will retry only one frame * at a time, until receiving ACK from receiving station, or reaching * retry limit and giving up. * * The command queue (#4/#9) must use this mode! * This mode does not require use of the Byte Count table in host DRAM. * * Driver controls scheduler operation via 3 means: * 1) Scheduler registers * 2) Shared scheduler data base in internal SRAM * 3) Shared data in host DRAM * * Initialization: * * When loading, driver should allocate memory for: * 1) 16 TFD circular buffers, each with space for (typically) 256 TFDs. * 2) 16 Byte Count circular buffers in 16 KBytes contiguous memory * (1024 bytes for each queue). * * After receiving "Alive" response from uCode, driver must initialize * the scheduler (especially for queue #4/#9, the command queue, otherwise * the driver can't issue commands!): */ #define IWM_SCD_MEM_LOWER_BOUND (0x0000) /** * Max Tx window size is the max number of contiguous TFDs that the scheduler * can keep track of at one time when creating block-ack chains of frames. * Note that "64" matches the number of ack bits in a block-ack packet. */ #define IWM_SCD_WIN_SIZE 64 #define IWM_SCD_FRAME_LIMIT 64 #define IWM_SCD_TXFIFO_POS_TID (0) #define IWM_SCD_TXFIFO_POS_RA (4) #define IWM_SCD_QUEUE_RA_TID_MAP_RATID_MSK (0x01FF) /* agn SCD */ #define IWM_SCD_QUEUE_STTS_REG_POS_TXF (0) #define IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE (3) #define IWM_SCD_QUEUE_STTS_REG_POS_WSL (4) #define IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN (19) #define IWM_SCD_QUEUE_STTS_REG_MSK (0x017F0000) #define IWM_SCD_QUEUE_CTX_REG1_CREDIT_POS (8) #define IWM_SCD_QUEUE_CTX_REG1_CREDIT_MSK (0x00FFFF00) #define IWM_SCD_QUEUE_CTX_REG1_SUPER_CREDIT_POS (24) #define IWM_SCD_QUEUE_CTX_REG1_SUPER_CREDIT_MSK (0xFF000000) #define IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS (0) #define IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) #define IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) #define IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) +#define IWM_SCD_GP_CTRL_ENABLE_31_QUEUES (1 << 0) +#define IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE (1 << 18) /* Context Data */ #define IWM_SCD_CONTEXT_MEM_LOWER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x600) #define IWM_SCD_CONTEXT_MEM_UPPER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x6A0) /* Tx status */ #define IWM_SCD_TX_STTS_MEM_LOWER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x6A0) #define IWM_SCD_TX_STTS_MEM_UPPER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x7E0) /* Translation Data */ #define IWM_SCD_TRANS_TBL_MEM_LOWER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x7E0) #define IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x808) #define IWM_SCD_CONTEXT_QUEUE_OFFSET(x)\ (IWM_SCD_CONTEXT_MEM_LOWER_BOUND + ((x) * 8)) #define IWM_SCD_TX_STTS_QUEUE_OFFSET(x)\ (IWM_SCD_TX_STTS_MEM_LOWER_BOUND + ((x) * 16)) #define IWM_SCD_TRANS_TBL_OFFSET_QUEUE(x) \ ((IWM_SCD_TRANS_TBL_MEM_LOWER_BOUND + ((x) * 2)) & 0xfffc) #define IWM_SCD_BASE (IWM_PRPH_BASE + 0xa02c00) #define IWM_SCD_SRAM_BASE_ADDR (IWM_SCD_BASE + 0x0) #define IWM_SCD_DRAM_BASE_ADDR (IWM_SCD_BASE + 0x8) #define IWM_SCD_AIT (IWM_SCD_BASE + 0x0c) #define IWM_SCD_TXFACT (IWM_SCD_BASE + 0x10) #define IWM_SCD_ACTIVE (IWM_SCD_BASE + 0x14) #define IWM_SCD_QUEUECHAIN_SEL (IWM_SCD_BASE + 0xe8) #define IWM_SCD_CHAINEXT_EN (IWM_SCD_BASE + 0x244) #define IWM_SCD_AGGR_SEL (IWM_SCD_BASE + 0x248) #define IWM_SCD_INTERRUPT_MASK (IWM_SCD_BASE + 0x108) +#define IWM_SCD_GP_CTRL (IWM_SCD_BASE + 0x1a8) +#define IWM_SCD_EN_CTRL (IWM_SCD_BASE + 0x254) static inline unsigned int IWM_SCD_QUEUE_WRPTR(unsigned int chnl) { if (chnl < 20) return IWM_SCD_BASE + 0x18 + chnl * 4; return IWM_SCD_BASE + 0x284 + (chnl - 20) * 4; } static inline unsigned int IWM_SCD_QUEUE_RDPTR(unsigned int chnl) { if (chnl < 20) return IWM_SCD_BASE + 0x68 + chnl * 4; return IWM_SCD_BASE + 0x2B4 + (chnl - 20) * 4; } static inline unsigned int IWM_SCD_QUEUE_STATUS_BITS(unsigned int chnl) { if (chnl < 20) return IWM_SCD_BASE + 0x10c + chnl * 4; return IWM_SCD_BASE + 0x384 + (chnl - 20) * 4; } /*********************** END TX SCHEDULER *************************************/ /* Oscillator clock */ #define IWM_OSC_CLK (0xa04068) #define IWM_OSC_CLK_FORCE_CONTROL (0x8) /* * END iwl-prph.h */ /* * BEGIN iwl-fh.h */ /****************************/ /* Flow Handler Definitions */ /****************************/ /** * This I/O area is directly read/writable by driver (e.g. Linux uses writel()) * Addresses are offsets from device's PCI hardware base address. */ #define IWM_FH_MEM_LOWER_BOUND (0x1000) #define IWM_FH_MEM_UPPER_BOUND (0x2000) /** * Keep-Warm (KW) buffer base address. * * Driver must allocate a 4KByte buffer that is for keeping the * host DRAM powered on (via dummy accesses to DRAM) to maintain low-latency * DRAM access when doing Txing or Rxing. The dummy accesses prevent host * from going into a power-savings mode that would cause higher DRAM latency, * and possible data over/under-runs, before all Tx/Rx is complete. * * Driver loads IWM_FH_KW_MEM_ADDR_REG with the physical address (bits 35:4) * of the buffer, which must be 4K aligned. Once this is set up, the device * automatically invokes keep-warm accesses when normal accesses might not * be sufficient to maintain fast DRAM response. * * Bit fields: * 31-0: Keep-warm buffer physical base address [35:4], must be 4K aligned */ #define IWM_FH_KW_MEM_ADDR_REG (IWM_FH_MEM_LOWER_BOUND + 0x97C) /** * TFD Circular Buffers Base (CBBC) addresses * * Device has 16 base pointer registers, one for each of 16 host-DRAM-resident * circular buffers (CBs/queues) containing Transmit Frame Descriptors (TFDs) * (see struct iwm_tfd_frame). These 16 pointer registers are offset by 0x04 * bytes from one another. Each TFD circular buffer in DRAM must be 256-byte * aligned (address bits 0-7 must be 0). * Later devices have 20 (5000 series) or 30 (higher) queues, but the registers * for them are in different places. * * Bit fields in each pointer register: * 27-0: TFD CB physical base address [35:8], must be 256-byte aligned */ #define IWM_FH_MEM_CBBC_0_15_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0x9D0) #define IWM_FH_MEM_CBBC_0_15_UPPER_BOUN (IWM_FH_MEM_LOWER_BOUND + 0xA10) #define IWM_FH_MEM_CBBC_16_19_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xBF0) #define IWM_FH_MEM_CBBC_16_19_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xC00) #define IWM_FH_MEM_CBBC_20_31_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xB20) #define IWM_FH_MEM_CBBC_20_31_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xB80) /* Find TFD CB base pointer for given queue */ static inline unsigned int IWM_FH_MEM_CBBC_QUEUE(unsigned int chnl) { if (chnl < 16) return IWM_FH_MEM_CBBC_0_15_LOWER_BOUND + 4 * chnl; if (chnl < 20) return IWM_FH_MEM_CBBC_16_19_LOWER_BOUND + 4 * (chnl - 16); return IWM_FH_MEM_CBBC_20_31_LOWER_BOUND + 4 * (chnl - 20); } /** * Rx SRAM Control and Status Registers (RSCSR) * * These registers provide handshake between driver and device for the Rx queue * (this queue handles *all* command responses, notifications, Rx data, etc. * sent from uCode to host driver). Unlike Tx, there is only one Rx * queue, and only one Rx DMA/FIFO channel. Also unlike Tx, which can * concatenate up to 20 DRAM buffers to form a Tx frame, each Receive Buffer * Descriptor (RBD) points to only one Rx Buffer (RB); there is a 1:1 * mapping between RBDs and RBs. * * Driver must allocate host DRAM memory for the following, and set the * physical address of each into device registers: * * 1) Receive Buffer Descriptor (RBD) circular buffer (CB), typically with 256 * entries (although any power of 2, up to 4096, is selectable by driver). * Each entry (1 dword) points to a receive buffer (RB) of consistent size * (typically 4K, although 8K or 16K are also selectable by driver). * Driver sets up RB size and number of RBDs in the CB via Rx config * register IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG. * * Bit fields within one RBD: * 27-0: Receive Buffer physical address bits [35:8], 256-byte aligned * * Driver sets physical address [35:8] of base of RBD circular buffer * into IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG [27:0]. * * 2) Rx status buffer, 8 bytes, in which uCode indicates which Rx Buffers * (RBs) have been filled, via a "write pointer", actually the index of * the RB's corresponding RBD within the circular buffer. Driver sets * physical address [35:4] into IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG [31:0]. * * Bit fields in lower dword of Rx status buffer (upper dword not used * by driver: * 31-12: Not used by driver * 11- 0: Index of last filled Rx buffer descriptor * (device writes, driver reads this value) * * As the driver prepares Receive Buffers (RBs) for device to fill, driver must * enter pointers to these RBs into contiguous RBD circular buffer entries, * and update the device's "write" index register, * IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG. * * This "write" index corresponds to the *next* RBD that the driver will make * available, i.e. one RBD past the tail of the ready-to-fill RBDs within * the circular buffer. This value should initially be 0 (before preparing any * RBs), should be 8 after preparing the first 8 RBs (for example), and must * wrap back to 0 at the end of the circular buffer (but don't wrap before * "read" index has advanced past 1! See below). * NOTE: DEVICE EXPECTS THE WRITE INDEX TO BE INCREMENTED IN MULTIPLES OF 8. * * As the device fills RBs (referenced from contiguous RBDs within the circular * buffer), it updates the Rx status buffer in host DRAM, 2) described above, * to tell the driver the index of the latest filled RBD. The driver must * read this "read" index from DRAM after receiving an Rx interrupt from device * * The driver must also internally keep track of a third index, which is the * next RBD to process. When receiving an Rx interrupt, driver should process * all filled but unprocessed RBs up to, but not including, the RB * corresponding to the "read" index. For example, if "read" index becomes "1", * driver may process the RB pointed to by RBD 0. Depending on volume of * traffic, there may be many RBs to process. * * If read index == write index, device thinks there is no room to put new data. * Due to this, the maximum number of filled RBs is 255, instead of 256. To * be safe, make sure that there is a gap of at least 2 RBDs between "write" * and "read" indexes; that is, make sure that there are no more than 254 * buffers waiting to be filled. */ #define IWM_FH_MEM_RSCSR_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xBC0) #define IWM_FH_MEM_RSCSR_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xC00) #define IWM_FH_MEM_RSCSR_CHNL0 (IWM_FH_MEM_RSCSR_LOWER_BOUND) /** * Physical base address of 8-byte Rx Status buffer. * Bit fields: * 31-0: Rx status buffer physical base address [35:4], must 16-byte aligned. */ #define IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG (IWM_FH_MEM_RSCSR_CHNL0) /** * Physical base address of Rx Buffer Descriptor Circular Buffer. * Bit fields: * 27-0: RBD CD physical base address [35:8], must be 256-byte aligned. */ #define IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG (IWM_FH_MEM_RSCSR_CHNL0 + 0x004) /** * Rx write pointer (index, really!). * Bit fields: * 11-0: Index of driver's most recent prepared-to-be-filled RBD, + 1. * NOTE: For 256-entry circular buffer, use only bits [7:0]. */ #define IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG (IWM_FH_MEM_RSCSR_CHNL0 + 0x008) #define IWM_FH_RSCSR_CHNL0_WPTR (IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG) #define IWM_FW_RSCSR_CHNL0_RXDCB_RDPTR_REG (IWM_FH_MEM_RSCSR_CHNL0 + 0x00c) #define IWM_FH_RSCSR_CHNL0_RDPTR IWM_FW_RSCSR_CHNL0_RXDCB_RDPTR_REG /** * Rx Config/Status Registers (RCSR) * Rx Config Reg for channel 0 (only channel used) * * Driver must initialize IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG as follows for * normal operation (see bit fields). * * Clearing IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG to 0 turns off Rx DMA. * Driver should poll IWM_FH_MEM_RSSR_RX_STATUS_REG for * IWM_FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (bit 24) before continuing. * * Bit fields: * 31-30: Rx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29-24: reserved * 23-20: # RBDs in circular buffer = 2^value; use "8" for 256 RBDs (normal), * min "5" for 32 RBDs, max "12" for 4096 RBDs. * 19-18: reserved * 17-16: size of each receive buffer; '00' 4K (normal), '01' 8K, * '10' 12K, '11' 16K. * 15-14: reserved * 13-12: IRQ destination; '00' none, '01' host driver (normal operation) * 11- 4: timeout for closing Rx buffer and interrupting host (units 32 usec) * typical value 0x10 (about 1/2 msec) * 3- 0: reserved */ #define IWM_FH_MEM_RCSR_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xC00) #define IWM_FH_MEM_RCSR_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xCC0) #define IWM_FH_MEM_RCSR_CHNL0 (IWM_FH_MEM_RCSR_LOWER_BOUND) #define IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG (IWM_FH_MEM_RCSR_CHNL0) #define IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR (IWM_FH_MEM_RCSR_CHNL0 + 0x8) #define IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ (IWM_FH_MEM_RCSR_CHNL0 + 0x10) #define IWM_FH_RCSR_CHNL0_RX_CONFIG_RB_TIMEOUT_MSK (0x00000FF0) /* bits 4-11 */ #define IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_MSK (0x00001000) /* bits 12 */ #define IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK (0x00008000) /* bit 15 */ #define IWM_FH_RCSR_CHNL0_RX_CONFIG_RB_SIZE_MSK (0x00030000) /* bits 16-17 */ #define IWM_FH_RCSR_CHNL0_RX_CONFIG_RBDBC_SIZE_MSK (0x00F00000) /* bits 20-23 */ #define IWM_FH_RCSR_CHNL0_RX_CONFIG_DMA_CHNL_EN_MSK (0xC0000000) /* bits 30-31*/ #define IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS (20) #define IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS (4) #define IWM_RX_RB_TIMEOUT (0x11) #define IWM_FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_VAL (0x00000000) #define IWM_FH_RCSR_RX_CONFIG_CHNL_EN_PAUSE_EOF_VAL (0x40000000) #define IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL (0x80000000) #define IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K (0x00000000) #define IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_8K (0x00010000) #define IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_12K (0x00020000) #define IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_16K (0x00030000) #define IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY (0x00000004) #define IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_NO_INT_VAL (0x00000000) #define IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL (0x00001000) /** * Rx Shared Status Registers (RSSR) * * After stopping Rx DMA channel (writing 0 to * IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG), driver must poll * IWM_FH_MEM_RSSR_RX_STATUS_REG until Rx channel is idle. * * Bit fields: * 24: 1 = Channel 0 is idle * * IWM_FH_MEM_RSSR_SHARED_CTRL_REG and IWM_FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV * contain default values that should not be altered by the driver. */ #define IWM_FH_MEM_RSSR_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xC40) #define IWM_FH_MEM_RSSR_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xD00) #define IWM_FH_MEM_RSSR_SHARED_CTRL_REG (IWM_FH_MEM_RSSR_LOWER_BOUND) #define IWM_FH_MEM_RSSR_RX_STATUS_REG (IWM_FH_MEM_RSSR_LOWER_BOUND + 0x004) #define IWM_FH_MEM_RSSR_RX_ENABLE_ERR_IRQ2DRV\ (IWM_FH_MEM_RSSR_LOWER_BOUND + 0x008) #define IWM_FH_RSSR_CHNL0_RX_STATUS_CHNL_IDLE (0x01000000) #define IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT 28 /* TFDB Area - TFDs buffer table */ #define IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK (0xFFFFFFFF) #define IWM_FH_TFDIB_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0x900) #define IWM_FH_TFDIB_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0x958) #define IWM_FH_TFDIB_CTRL0_REG(_chnl) (IWM_FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl)) #define IWM_FH_TFDIB_CTRL1_REG(_chnl) (IWM_FH_TFDIB_LOWER_BOUND + 0x8 * (_chnl) + 0x4) /** * Transmit DMA Channel Control/Status Registers (TCSR) * * Device has one configuration register for each of 8 Tx DMA/FIFO channels * supported in hardware (don't confuse these with the 16 Tx queues in DRAM, * which feed the DMA/FIFO channels); config regs are separated by 0x20 bytes. * * To use a Tx DMA channel, driver must initialize its * IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl) with: * * IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | * IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE_VAL * * All other bits should be 0. * * Bit fields: * 31-30: Tx DMA channel enable: '00' off/pause, '01' pause at end of frame, * '10' operate normally * 29- 4: Reserved, set to "0" * 3: Enable internal DMA requests (1, normal operation), disable (0) * 2- 0: Reserved, set to "0" */ #define IWM_FH_TCSR_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xD00) #define IWM_FH_TCSR_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xE60) /* Find Control/Status reg for given Tx DMA/FIFO channel */ #define IWM_FH_TCSR_CHNL_NUM (8) /* TCSR: tx_config register values */ #define IWM_FH_TCSR_CHNL_TX_CONFIG_REG(_chnl) \ (IWM_FH_TCSR_LOWER_BOUND + 0x20 * (_chnl)) #define IWM_FH_TCSR_CHNL_TX_CREDIT_REG(_chnl) \ (IWM_FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x4) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(_chnl) \ (IWM_FH_TCSR_LOWER_BOUND + 0x20 * (_chnl) + 0x8) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_TXF (0x00000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_MSG_MODE_DRV (0x00000001) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE (0x00000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE (0x00000008) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_NOINT (0x00000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD (0x00100000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_IFTFD (0x00200000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_NOINT (0x00000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_ENDTFD (0x00400000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_RTC_IFTFD (0x00800000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE (0x00000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE_EOF (0x40000000) #define IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE (0x80000000) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_EMPTY (0x00000000) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_WAIT (0x00002000) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID (0x00000003) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM (20) #define IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX (12) /** * Tx Shared Status Registers (TSSR) * * After stopping Tx DMA channel (writing 0 to * IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl)), driver must poll * IWM_FH_TSSR_TX_STATUS_REG until selected Tx channel is idle * (channel's buffers empty | no pending requests). * * Bit fields: * 31-24: 1 = Channel buffers empty (channel 7:0) * 23-16: 1 = No pending requests (channel 7:0) */ #define IWM_FH_TSSR_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xEA0) #define IWM_FH_TSSR_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0xEC0) #define IWM_FH_TSSR_TX_STATUS_REG (IWM_FH_TSSR_LOWER_BOUND + 0x010) /** * Bit fields for TSSR(Tx Shared Status & Control) error status register: * 31: Indicates an address error when accessed to internal memory * uCode/driver must write "1" in order to clear this flag * 30: Indicates that Host did not send the expected number of dwords to FH * uCode/driver must write "1" in order to clear this flag * 16-9:Each status bit is for one channel. Indicates that an (Error) ActDMA * command was received from the scheduler while the TRB was already full * with previous command * uCode/driver must write "1" in order to clear this flag * 7-0: Each status bit indicates a channel's TxCredit error. When an error * bit is set, it indicates that the FH has received a full indication * from the RTC TxFIFO and the current value of the TxCredit counter was * not equal to zero. This mean that the credit mechanism was not * synchronized to the TxFIFO status * uCode/driver must write "1" in order to clear this flag */ #define IWM_FH_TSSR_TX_ERROR_REG (IWM_FH_TSSR_LOWER_BOUND + 0x018) #define IWM_FH_TSSR_TX_MSG_CONFIG_REG (IWM_FH_TSSR_LOWER_BOUND + 0x008) #define IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(_chnl) ((1 << (_chnl)) << 16) /* Tx service channels */ #define IWM_FH_SRVC_CHNL (9) #define IWM_FH_SRVC_LOWER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0x9C8) #define IWM_FH_SRVC_UPPER_BOUND (IWM_FH_MEM_LOWER_BOUND + 0x9D0) #define IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(_chnl) \ (IWM_FH_SRVC_LOWER_BOUND + ((_chnl) - 9) * 0x4) #define IWM_FH_TX_CHICKEN_BITS_REG (IWM_FH_MEM_LOWER_BOUND + 0xE98) #define IWM_FH_TX_TRB_REG(_chan) (IWM_FH_MEM_LOWER_BOUND + 0x958 + \ (_chan) * 4) /* Instruct FH to increment the retry count of a packet when * it is brought from the memory to TX-FIFO */ #define IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN (0x00000002) #define IWM_RX_QUEUE_SIZE 256 #define IWM_RX_QUEUE_MASK 255 #define IWM_RX_QUEUE_SIZE_LOG 8 /* * RX related structures and functions */ #define IWM_RX_FREE_BUFFERS 64 #define IWM_RX_LOW_WATERMARK 8 /** * struct iwm_rb_status - reseve buffer status * host memory mapped FH registers * @closed_rb_num [0:11] - Indicates the index of the RB which was closed * @closed_fr_num [0:11] - Indicates the index of the RX Frame which was closed * @finished_rb_num [0:11] - Indicates the index of the current RB * in which the last frame was written to * @finished_fr_num [0:11] - Indicates the index of the RX Frame * which was transferred */ struct iwm_rb_status { uint16_t closed_rb_num; uint16_t closed_fr_num; uint16_t finished_rb_num; uint16_t finished_fr_nam; uint32_t unused; } __packed; #define IWM_TFD_QUEUE_SIZE_MAX (256) #define IWM_TFD_QUEUE_SIZE_BC_DUP (64) #define IWM_TFD_QUEUE_BC_SIZE (IWM_TFD_QUEUE_SIZE_MAX + \ IWM_TFD_QUEUE_SIZE_BC_DUP) #define IWM_TX_DMA_MASK DMA_BIT_MASK(36) #define IWM_NUM_OF_TBS 20 static inline uint8_t iwm_get_dma_hi_addr(bus_addr_t addr) { return (sizeof(addr) > sizeof(uint32_t) ? (addr >> 16) >> 16 : 0) & 0xF; } /** * struct iwm_tfd_tb transmit buffer descriptor within transmit frame descriptor * * This structure contains dma address and length of transmission address * * @lo: low [31:0] portion of the dma address of TX buffer * every even is unaligned on 16 bit boundary * @hi_n_len 0-3 [35:32] portion of dma * 4-15 length of the tx buffer */ struct iwm_tfd_tb { uint32_t lo; uint16_t hi_n_len; } __packed; /** * struct iwm_tfd * * Transmit Frame Descriptor (TFD) * * @ __reserved1[3] reserved * @ num_tbs 0-4 number of active tbs * 5 reserved * 6-7 padding (not used) * @ tbs[20] transmit frame buffer descriptors * @ __pad padding * * Each Tx queue uses a circular buffer of 256 TFDs stored in host DRAM. * Both driver and device share these circular buffers, each of which must be * contiguous 256 TFDs x 128 bytes-per-TFD = 32 KBytes * * Driver must indicate the physical address of the base of each * circular buffer via the IWM_FH_MEM_CBBC_QUEUE registers. * * Each TFD contains pointer/size information for up to 20 data buffers * in host DRAM. These buffers collectively contain the (one) frame described * by the TFD. Each buffer must be a single contiguous block of memory within * itself, but buffers may be scattered in host DRAM. Each buffer has max size * of (4K - 4). The concatenates all of a TFD's buffers into a single * Tx frame, up to 8 KBytes in size. * * A maximum of 255 (not 256!) TFDs may be on a queue waiting for Tx. */ struct iwm_tfd { uint8_t __reserved1[3]; uint8_t num_tbs; struct iwm_tfd_tb tbs[IWM_NUM_OF_TBS]; uint32_t __pad; } __packed; /* Keep Warm Size */ #define IWM_KW_SIZE 0x1000 /* 4k */ /* Fixed (non-configurable) rx data from phy */ /** * struct iwm_agn_schedq_bc_tbl scheduler byte count table * base physical address provided by IWM_SCD_DRAM_BASE_ADDR * @tfd_offset 0-12 - tx command byte count * 12-16 - station index */ struct iwm_agn_scd_bc_tbl { uint16_t tfd_offset[IWM_TFD_QUEUE_BC_SIZE]; } __packed; /* * END iwl-fh.h */ /* * BEGIN mvm/fw-api.h */ -/* maximal number of Tx queues in any platform */ -#define IWM_MVM_MAX_QUEUES 20 +/* Maximum number of Tx queues. */ +#define IWM_MVM_MAX_QUEUES 31 /* Tx queue numbers */ enum { IWM_MVM_OFFCHANNEL_QUEUE = 8, IWM_MVM_CMD_QUEUE = 9, + IWM_MVM_AUX_QUEUE = 15, }; enum iwm_mvm_tx_fifo { IWM_MVM_TX_FIFO_BK = 0, IWM_MVM_TX_FIFO_BE, IWM_MVM_TX_FIFO_VI, IWM_MVM_TX_FIFO_VO, IWM_MVM_TX_FIFO_MCAST = 5, IWM_MVM_TX_FIFO_CMD = 7, }; #define IWM_MVM_STATION_COUNT 16 /* commands */ enum { IWM_MVM_ALIVE = 0x1, IWM_REPLY_ERROR = 0x2, IWM_INIT_COMPLETE_NOTIF = 0x4, /* PHY context commands */ IWM_PHY_CONTEXT_CMD = 0x8, IWM_DBG_CFG = 0x9, + /* UMAC scan commands */ + IWM_SCAN_ITERATION_COMPLETE_UMAC = 0xb5, + IWM_SCAN_CFG_CMD = 0xc, + IWM_SCAN_REQ_UMAC = 0xd, + IWM_SCAN_ABORT_UMAC = 0xe, + IWM_SCAN_COMPLETE_UMAC = 0xf, + /* station table */ IWM_ADD_STA_KEY = 0x17, IWM_ADD_STA = 0x18, IWM_REMOVE_STA = 0x19, /* TX */ IWM_TX_CMD = 0x1c, IWM_TXPATH_FLUSH = 0x1e, IWM_MGMT_MCAST_KEY = 0x1f, + /* scheduler config */ + IWM_SCD_QUEUE_CFG = 0x1d, + /* global key */ IWM_WEP_KEY = 0x20, /* MAC and Binding commands */ IWM_MAC_CONTEXT_CMD = 0x28, IWM_TIME_EVENT_CMD = 0x29, /* both CMD and response */ IWM_TIME_EVENT_NOTIFICATION = 0x2a, IWM_BINDING_CONTEXT_CMD = 0x2b, IWM_TIME_QUOTA_CMD = 0x2c, IWM_NON_QOS_TX_COUNTER_CMD = 0x2d, IWM_LQ_CMD = 0x4e, /* Calibration */ IWM_TEMPERATURE_NOTIFICATION = 0x62, IWM_CALIBRATION_CFG_CMD = 0x65, IWM_CALIBRATION_RES_NOTIFICATION = 0x66, IWM_CALIBRATION_COMPLETE_NOTIFICATION = 0x67, IWM_RADIO_VERSION_NOTIFICATION = 0x68, /* Scan offload */ IWM_SCAN_OFFLOAD_REQUEST_CMD = 0x51, IWM_SCAN_OFFLOAD_ABORT_CMD = 0x52, - IWM_SCAN_OFFLOAD_COMPLETE = 0x6D, - IWM_SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + IWM_HOT_SPOT_CMD = 0x53, + IWM_SCAN_OFFLOAD_COMPLETE = 0x6d, + IWM_SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6e, IWM_SCAN_OFFLOAD_CONFIG_CMD = 0x6f, IWM_MATCH_FOUND_NOTIFICATION = 0xd9, + IWM_SCAN_ITERATION_COMPLETE = 0xe7, /* Phy */ IWM_PHY_CONFIGURATION_CMD = 0x6a, IWM_CALIB_RES_NOTIF_PHY_DB = 0x6b, /* IWM_PHY_DB_CMD = 0x6c, */ /* Power - legacy power table command */ IWM_POWER_TABLE_CMD = 0x77, IWM_PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, /* Thermal Throttling*/ IWM_REPLY_THERMAL_MNG_BACKOFF = 0x7e, /* Scanning */ IWM_SCAN_REQUEST_CMD = 0x80, IWM_SCAN_ABORT_CMD = 0x81, IWM_SCAN_START_NOTIFICATION = 0x82, IWM_SCAN_RESULTS_NOTIFICATION = 0x83, IWM_SCAN_COMPLETE_NOTIFICATION = 0x84, /* NVM */ IWM_NVM_ACCESS_CMD = 0x88, IWM_SET_CALIB_DEFAULT_CMD = 0x8e, IWM_BEACON_NOTIFICATION = 0x90, IWM_BEACON_TEMPLATE_CMD = 0x91, IWM_TX_ANT_CONFIGURATION_CMD = 0x98, IWM_BT_CONFIG = 0x9b, IWM_STATISTICS_NOTIFICATION = 0x9d, IWM_REDUCE_TX_POWER_CMD = 0x9f, /* RF-KILL commands and notifications */ IWM_CARD_STATE_CMD = 0xa0, IWM_CARD_STATE_NOTIFICATION = 0xa1, IWM_MISSED_BEACONS_NOTIFICATION = 0xa2, + IWM_MFUART_LOAD_NOTIFICATION = 0xb1, + /* Power - new power table command */ IWM_MAC_PM_POWER_TABLE = 0xa9, IWM_REPLY_RX_PHY_CMD = 0xc0, IWM_REPLY_RX_MPDU_CMD = 0xc1, IWM_BA_NOTIF = 0xc5, + /* Location Aware Regulatory */ + IWM_MCC_UPDATE_CMD = 0xc8, + IWM_MCC_CHUB_UPDATE_CMD = 0xc9, + /* BT Coex */ IWM_BT_COEX_PRIO_TABLE = 0xcc, IWM_BT_COEX_PROT_ENV = 0xcd, IWM_BT_PROFILE_NOTIFICATION = 0xce, IWM_BT_COEX_CI = 0x5d, IWM_REPLY_SF_CFG_CMD = 0xd1, IWM_REPLY_BEACON_FILTERING_CMD = 0xd2, + /* DTS measurements */ + IWM_CMD_DTS_MEASUREMENT_TRIGGER = 0xdc, + IWM_DTS_MEASUREMENT_NOTIFICATION = 0xdd, + IWM_REPLY_DEBUG_CMD = 0xf0, IWM_DEBUG_LOG_MSG = 0xf7, IWM_MCAST_FILTER_CMD = 0xd0, /* D3 commands/notifications */ IWM_D3_CONFIG_CMD = 0xd3, IWM_PROT_OFFLOAD_CONFIG_CMD = 0xd4, IWM_OFFLOADS_QUERY_CMD = 0xd5, IWM_REMOTE_WAKE_CONFIG_CMD = 0xd6, /* for WoWLAN in particular */ IWM_WOWLAN_PATTERNS = 0xe0, IWM_WOWLAN_CONFIGURATION = 0xe1, IWM_WOWLAN_TSC_RSC_PARAM = 0xe2, IWM_WOWLAN_TKIP_PARAM = 0xe3, IWM_WOWLAN_KEK_KCK_MATERIAL = 0xe4, IWM_WOWLAN_GET_STATUSES = 0xe5, IWM_WOWLAN_TX_POWER_PER_DB = 0xe6, /* and for NetDetect */ IWM_NET_DETECT_CONFIG_CMD = 0x54, IWM_NET_DETECT_PROFILES_QUERY_CMD = 0x56, IWM_NET_DETECT_PROFILES_CMD = 0x57, IWM_NET_DETECT_HOTSPOTS_CMD = 0x58, IWM_NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59, IWM_REPLY_MAX = 0xff, }; /** * struct iwm_cmd_response - generic response struct for most commands * @status: status of the command asked, changes for each one */ struct iwm_cmd_response { uint32_t status; }; /* * struct iwm_tx_ant_cfg_cmd * @valid: valid antenna configuration */ struct iwm_tx_ant_cfg_cmd { uint32_t valid; } __packed; /** * struct iwm_reduce_tx_power_cmd - TX power reduction command * IWM_REDUCE_TX_POWER_CMD = 0x9f * @flags: (reserved for future implementation) * @mac_context_id: id of the mac ctx for which we are reducing TX power. * @pwr_restriction: TX power restriction in dBms. */ struct iwm_reduce_tx_power_cmd { uint8_t flags; uint8_t mac_context_id; uint16_t pwr_restriction; } __packed; /* IWM_TX_REDUCED_POWER_API_S_VER_1 */ /* * Calibration control struct. * Sent as part of the phy configuration command. * @flow_trigger: bitmap for which calibrations to perform according to * flow triggers. * @event_trigger: bitmap for which calibrations to perform according to * event triggers. */ struct iwm_calib_ctrl { uint32_t flow_trigger; uint32_t event_trigger; } __packed; /* This enum defines the bitmap of various calibrations to enable in both * init ucode and runtime ucode through IWM_CALIBRATION_CFG_CMD. */ enum iwm_calib_cfg { IWM_CALIB_CFG_XTAL_IDX = (1 << 0), IWM_CALIB_CFG_TEMPERATURE_IDX = (1 << 1), IWM_CALIB_CFG_VOLTAGE_READ_IDX = (1 << 2), IWM_CALIB_CFG_PAPD_IDX = (1 << 3), IWM_CALIB_CFG_TX_PWR_IDX = (1 << 4), IWM_CALIB_CFG_DC_IDX = (1 << 5), IWM_CALIB_CFG_BB_FILTER_IDX = (1 << 6), IWM_CALIB_CFG_LO_LEAKAGE_IDX = (1 << 7), IWM_CALIB_CFG_TX_IQ_IDX = (1 << 8), IWM_CALIB_CFG_TX_IQ_SKEW_IDX = (1 << 9), IWM_CALIB_CFG_RX_IQ_IDX = (1 << 10), IWM_CALIB_CFG_RX_IQ_SKEW_IDX = (1 << 11), IWM_CALIB_CFG_SENSITIVITY_IDX = (1 << 12), IWM_CALIB_CFG_CHAIN_NOISE_IDX = (1 << 13), IWM_CALIB_CFG_DISCONNECTED_ANT_IDX = (1 << 14), IWM_CALIB_CFG_ANT_COUPLING_IDX = (1 << 15), IWM_CALIB_CFG_DAC_IDX = (1 << 16), IWM_CALIB_CFG_ABS_IDX = (1 << 17), IWM_CALIB_CFG_AGC_IDX = (1 << 18), }; /* * Phy configuration command. */ struct iwm_phy_cfg_cmd { uint32_t phy_cfg; struct iwm_calib_ctrl calib_control; } __packed; #define IWM_PHY_CFG_RADIO_TYPE ((1 << 0) | (1 << 1)) #define IWM_PHY_CFG_RADIO_STEP ((1 << 2) | (1 << 3)) #define IWM_PHY_CFG_RADIO_DASH ((1 << 4) | (1 << 5)) #define IWM_PHY_CFG_PRODUCT_NUMBER ((1 << 6) | (1 << 7)) #define IWM_PHY_CFG_TX_CHAIN_A (1 << 8) #define IWM_PHY_CFG_TX_CHAIN_B (1 << 9) #define IWM_PHY_CFG_TX_CHAIN_C (1 << 10) #define IWM_PHY_CFG_RX_CHAIN_A (1 << 12) #define IWM_PHY_CFG_RX_CHAIN_B (1 << 13) #define IWM_PHY_CFG_RX_CHAIN_C (1 << 14) /* * PHY db */ enum iwm_phy_db_section_type { IWM_PHY_DB_CFG = 1, IWM_PHY_DB_CALIB_NCH, IWM_PHY_DB_UNUSED, IWM_PHY_DB_CALIB_CHG_PAPD, IWM_PHY_DB_CALIB_CHG_TXP, IWM_PHY_DB_MAX }; #define IWM_PHY_DB_CMD 0x6c /* TEMP API - The actual is 0x8c */ /* * phy db - configure operational ucode */ struct iwm_phy_db_cmd { uint16_t type; uint16_t length; uint8_t data[]; } __packed; /* for parsing of tx power channel group data that comes from the firmware */ struct iwm_phy_db_chg_txp { uint32_t space; uint16_t max_channel_idx; } __packed; /* * phy db - Receive phy db chunk after calibrations */ struct iwm_calib_res_notif_phy_db { uint16_t type; uint16_t length; uint8_t data[]; } __packed; /* Target of the IWM_NVM_ACCESS_CMD */ enum { IWM_NVM_ACCESS_TARGET_CACHE = 0, IWM_NVM_ACCESS_TARGET_OTP = 1, IWM_NVM_ACCESS_TARGET_EEPROM = 2, }; /* Section types for IWM_NVM_ACCESS_CMD */ enum { IWM_NVM_SECTION_TYPE_HW = 0, IWM_NVM_SECTION_TYPE_SW, IWM_NVM_SECTION_TYPE_PAPD, - IWM_NVM_SECTION_TYPE_BT, + IWM_NVM_SECTION_TYPE_REGULATORY, IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, IWM_NVM_SECTION_TYPE_POST_FCS_CALIB, + /* 7, 8, 9 unknown */ + IWM_NVM_SECTION_TYPE_HW_8000 = 10, + IWM_NVM_SECTION_TYPE_MAC_OVERRIDE, + IWM_NVM_SECTION_TYPE_PHY_SKU, IWM_NVM_NUM_OF_SECTIONS, }; /** * struct iwm_nvm_access_cmd_ver2 - Request the device to send an NVM section * @op_code: 0 - read, 1 - write * @target: IWM_NVM_ACCESS_TARGET_* * @type: IWM_NVM_SECTION_TYPE_* * @offset: offset in bytes into the section * @length: in bytes, to read/write * @data: if write operation, the data to write. On read its empty */ struct iwm_nvm_access_cmd { uint8_t op_code; uint8_t target; uint16_t type; uint16_t offset; uint16_t length; uint8_t data[]; } __packed; /* IWM_NVM_ACCESS_CMD_API_S_VER_2 */ /** * struct iwm_nvm_access_resp_ver2 - response to IWM_NVM_ACCESS_CMD * @offset: offset in bytes into the section * @length: in bytes, either how much was written or read * @type: IWM_NVM_SECTION_TYPE_* * @status: 0 for success, fail otherwise * @data: if read operation, the data returned. Empty on write. */ struct iwm_nvm_access_resp { uint16_t offset; uint16_t length; uint16_t type; uint16_t status; uint8_t data[]; } __packed; /* IWM_NVM_ACCESS_CMD_RESP_API_S_VER_2 */ /* IWM_MVM_ALIVE 0x1 */ /* alive response is_valid values */ #define IWM_ALIVE_RESP_UCODE_OK (1 << 0) #define IWM_ALIVE_RESP_RFKILL (1 << 1) /* alive response ver_type values */ enum { IWM_FW_TYPE_HW = 0, IWM_FW_TYPE_PROT = 1, IWM_FW_TYPE_AP = 2, IWM_FW_TYPE_WOWLAN = 3, IWM_FW_TYPE_TIMING = 4, IWM_FW_TYPE_WIPAN = 5 }; /* alive response ver_subtype values */ enum { IWM_FW_SUBTYPE_FULL_FEATURE = 0, IWM_FW_SUBTYPE_BOOTSRAP = 1, /* Not valid */ IWM_FW_SUBTYPE_REDUCED = 2, IWM_FW_SUBTYPE_ALIVE_ONLY = 3, IWM_FW_SUBTYPE_WOWLAN = 4, IWM_FW_SUBTYPE_AP_SUBTYPE = 5, IWM_FW_SUBTYPE_WIPAN = 6, IWM_FW_SUBTYPE_INITIALIZE = 9 }; #define IWM_ALIVE_STATUS_ERR 0xDEAD #define IWM_ALIVE_STATUS_OK 0xCAFE #define IWM_ALIVE_FLG_RFKILL (1 << 0) -struct iwm_mvm_alive_resp { +struct iwm_mvm_alive_resp_v1 { uint16_t status; uint16_t flags; uint8_t ucode_minor; uint8_t ucode_major; uint16_t id; uint8_t api_minor; uint8_t api_major; uint8_t ver_subtype; uint8_t ver_type; uint8_t mac; uint8_t opt; uint16_t reserved2; uint32_t timestamp; uint32_t error_event_table_ptr; /* SRAM address for error log */ uint32_t log_event_table_ptr; /* SRAM address for event log */ uint32_t cpu_register_ptr; uint32_t dbgm_config_ptr; uint32_t alive_counter_ptr; uint32_t scd_base_ptr; /* SRAM address for SCD */ } __packed; /* IWM_ALIVE_RES_API_S_VER_1 */ +struct iwm_mvm_alive_resp_v2 { + uint16_t status; + uint16_t flags; + uint8_t ucode_minor; + uint8_t ucode_major; + uint16_t id; + uint8_t api_minor; + uint8_t api_major; + uint8_t ver_subtype; + uint8_t ver_type; + uint8_t mac; + uint8_t opt; + uint16_t reserved2; + uint32_t timestamp; + uint32_t error_event_table_ptr; /* SRAM address for error log */ + uint32_t log_event_table_ptr; /* SRAM address for LMAC event log */ + uint32_t cpu_register_ptr; + uint32_t dbgm_config_ptr; + uint32_t alive_counter_ptr; + uint32_t scd_base_ptr; /* SRAM address for SCD */ + uint32_t st_fwrd_addr; /* pointer to Store and forward */ + uint32_t st_fwrd_size; + uint8_t umac_minor; /* UMAC version: minor */ + uint8_t umac_major; /* UMAC version: major */ + uint16_t umac_id; /* UMAC version: id */ + uint32_t error_info_addr; /* SRAM address for UMAC error log */ + uint32_t dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_2 */ + +struct iwm_mvm_alive_resp_v3 { + uint16_t status; + uint16_t flags; + uint32_t ucode_minor; + uint32_t ucode_major; + uint8_t ver_subtype; + uint8_t ver_type; + uint8_t mac; + uint8_t opt; + uint32_t timestamp; + uint32_t error_event_table_ptr; /* SRAM address for error log */ + uint32_t log_event_table_ptr; /* SRAM address for LMAC event log */ + uint32_t cpu_register_ptr; + uint32_t dbgm_config_ptr; + uint32_t alive_counter_ptr; + uint32_t scd_base_ptr; /* SRAM address for SCD */ + uint32_t st_fwrd_addr; /* pointer to Store and forward */ + uint32_t st_fwrd_size; + uint32_t umac_minor; /* UMAC version: minor */ + uint32_t umac_major; /* UMAC version: major */ + uint32_t error_info_addr; /* SRAM address for UMAC error log */ + uint32_t dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_3 */ + /* Error response/notification */ enum { IWM_FW_ERR_UNKNOWN_CMD = 0x0, IWM_FW_ERR_INVALID_CMD_PARAM = 0x1, IWM_FW_ERR_SERVICE = 0x2, IWM_FW_ERR_ARC_MEMORY = 0x3, IWM_FW_ERR_ARC_CODE = 0x4, IWM_FW_ERR_WATCH_DOG = 0x5, IWM_FW_ERR_WEP_GRP_KEY_INDX = 0x10, IWM_FW_ERR_WEP_KEY_SIZE = 0x11, IWM_FW_ERR_OBSOLETE_FUNC = 0x12, IWM_FW_ERR_UNEXPECTED = 0xFE, IWM_FW_ERR_FATAL = 0xFF }; /** * struct iwm_error_resp - FW error indication * ( IWM_REPLY_ERROR = 0x2 ) * @error_type: one of IWM_FW_ERR_* * @cmd_id: the command ID for which the error occurred * @bad_cmd_seq_num: sequence number of the erroneous command * @error_service: which service created the error, applicable only if * error_type = 2, otherwise 0 * @timestamp: TSF in usecs. */ struct iwm_error_resp { uint32_t error_type; uint8_t cmd_id; uint8_t reserved1; uint16_t bad_cmd_seq_num; uint32_t error_service; uint64_t timestamp; } __packed; /* Common PHY, MAC and Bindings definitions */ #define IWM_MAX_MACS_IN_BINDING (3) #define IWM_MAX_BINDINGS (4) #define IWM_AUX_BINDING_INDEX (3) #define IWM_MAX_PHYS (4) /* Used to extract ID and color from the context dword */ #define IWM_FW_CTXT_ID_POS (0) #define IWM_FW_CTXT_ID_MSK (0xff << IWM_FW_CTXT_ID_POS) #define IWM_FW_CTXT_COLOR_POS (8) #define IWM_FW_CTXT_COLOR_MSK (0xff << IWM_FW_CTXT_COLOR_POS) #define IWM_FW_CTXT_INVALID (0xffffffff) #define IWM_FW_CMD_ID_AND_COLOR(_id, _color) ((_id << IWM_FW_CTXT_ID_POS) |\ (_color << IWM_FW_CTXT_COLOR_POS)) /* Possible actions on PHYs, MACs and Bindings */ enum { IWM_FW_CTXT_ACTION_STUB = 0, IWM_FW_CTXT_ACTION_ADD, IWM_FW_CTXT_ACTION_MODIFY, IWM_FW_CTXT_ACTION_REMOVE, IWM_FW_CTXT_ACTION_NUM }; /* COMMON_CONTEXT_ACTION_API_E_VER_1 */ /* Time Events */ /* Time Event types, according to MAC type */ enum iwm_time_event_type { /* BSS Station Events */ IWM_TE_BSS_STA_AGGRESSIVE_ASSOC, IWM_TE_BSS_STA_ASSOC, IWM_TE_BSS_EAP_DHCP_PROT, IWM_TE_BSS_QUIET_PERIOD, /* P2P Device Events */ IWM_TE_P2P_DEVICE_DISCOVERABLE, IWM_TE_P2P_DEVICE_LISTEN, IWM_TE_P2P_DEVICE_ACTION_SCAN, IWM_TE_P2P_DEVICE_FULL_SCAN, /* P2P Client Events */ IWM_TE_P2P_CLIENT_AGGRESSIVE_ASSOC, IWM_TE_P2P_CLIENT_ASSOC, IWM_TE_P2P_CLIENT_QUIET_PERIOD, /* P2P GO Events */ IWM_TE_P2P_GO_ASSOC_PROT, IWM_TE_P2P_GO_REPETITIVE_NOA, IWM_TE_P2P_GO_CT_WINDOW, /* WiDi Sync Events */ IWM_TE_WIDI_TX_SYNC, IWM_TE_MAX }; /* IWM_MAC_EVENT_TYPE_API_E_VER_1 */ /* Time event - defines for command API v1 */ /* * @IWM_TE_V1_FRAG_NONE: fragmentation of the time event is NOT allowed. * @IWM_TE_V1_FRAG_SINGLE: fragmentation of the time event is allowed, but only * the first fragment is scheduled. * @IWM_TE_V1_FRAG_DUAL: fragmentation of the time event is allowed, but only * the first 2 fragments are scheduled. * @IWM_TE_V1_FRAG_ENDLESS: fragmentation of the time event is allowed, and any * number of fragments are valid. * * Other than the constant defined above, specifying a fragmentation value 'x' * means that the event can be fragmented but only the first 'x' will be * scheduled. */ enum { IWM_TE_V1_FRAG_NONE = 0, IWM_TE_V1_FRAG_SINGLE = 1, IWM_TE_V1_FRAG_DUAL = 2, IWM_TE_V1_FRAG_ENDLESS = 0xffffffff }; /* If a Time Event can be fragmented, this is the max number of fragments */ #define IWM_TE_V1_FRAG_MAX_MSK 0x0fffffff /* Repeat the time event endlessly (until removed) */ #define IWM_TE_V1_REPEAT_ENDLESS 0xffffffff /* If a Time Event has bounded repetitions, this is the maximal value */ #define IWM_TE_V1_REPEAT_MAX_MSK_V1 0x0fffffff /* Time Event dependencies: none, on another TE, or in a specific time */ enum { IWM_TE_V1_INDEPENDENT = 0, IWM_TE_V1_DEP_OTHER = (1 << 0), IWM_TE_V1_DEP_TSF = (1 << 1), IWM_TE_V1_EVENT_SOCIOPATHIC = (1 << 2), }; /* IWM_MAC_EVENT_DEPENDENCY_POLICY_API_E_VER_2 */ /* * @IWM_TE_V1_NOTIF_NONE: no notifications * @IWM_TE_V1_NOTIF_HOST_EVENT_START: request/receive notification on event start * @IWM_TE_V1_NOTIF_HOST_EVENT_END:request/receive notification on event end * @IWM_TE_V1_NOTIF_INTERNAL_EVENT_START: internal FW use * @IWM_TE_V1_NOTIF_INTERNAL_EVENT_END: internal FW use. * @IWM_TE_V1_NOTIF_HOST_FRAG_START: request/receive notification on frag start * @IWM_TE_V1_NOTIF_HOST_FRAG_END:request/receive notification on frag end * @IWM_TE_V1_NOTIF_INTERNAL_FRAG_START: internal FW use. * @IWM_TE_V1_NOTIF_INTERNAL_FRAG_END: internal FW use. * * Supported Time event notifications configuration. * A notification (both event and fragment) includes a status indicating weather * the FW was able to schedule the event or not. For fragment start/end * notification the status is always success. There is no start/end fragment * notification for monolithic events. */ enum { IWM_TE_V1_NOTIF_NONE = 0, IWM_TE_V1_NOTIF_HOST_EVENT_START = (1 << 0), IWM_TE_V1_NOTIF_HOST_EVENT_END = (1 << 1), IWM_TE_V1_NOTIF_INTERNAL_EVENT_START = (1 << 2), IWM_TE_V1_NOTIF_INTERNAL_EVENT_END = (1 << 3), IWM_TE_V1_NOTIF_HOST_FRAG_START = (1 << 4), IWM_TE_V1_NOTIF_HOST_FRAG_END = (1 << 5), IWM_TE_V1_NOTIF_INTERNAL_FRAG_START = (1 << 6), IWM_TE_V1_NOTIF_INTERNAL_FRAG_END = (1 << 7), + IWM_T2_V2_START_IMMEDIATELY = (1 << 11), }; /* IWM_MAC_EVENT_ACTION_API_E_VER_2 */ /** * struct iwm_time_event_cmd_api_v1 - configuring Time Events * with struct IWM_MAC_TIME_EVENT_DATA_API_S_VER_1 (see also * with version 2. determined by IWM_UCODE_TLV_FLAGS) * ( IWM_TIME_EVENT_CMD = 0x29 ) * @id_and_color: ID and color of the relevant MAC * @action: action to perform, one of IWM_FW_CTXT_ACTION_* * @id: this field has two meanings, depending on the action: * If the action is ADD, then it means the type of event to add. * For all other actions it is the unique event ID assigned when the * event was added by the FW. * @apply_time: When to start the Time Event (in GP2) * @max_delay: maximum delay to event's start (apply time), in TU * @depends_on: the unique ID of the event we depend on (if any) * @interval: interval between repetitions, in TU * @interval_reciprocal: 2^32 / interval * @duration: duration of event in TU * @repeat: how many repetitions to do, can be IWM_TE_REPEAT_ENDLESS * @dep_policy: one of IWM_TE_V1_INDEPENDENT, IWM_TE_V1_DEP_OTHER, IWM_TE_V1_DEP_TSF * and IWM_TE_V1_EVENT_SOCIOPATHIC * @is_present: 0 or 1, are we present or absent during the Time Event * @max_frags: maximal number of fragments the Time Event can be divided to * @notify: notifications using IWM_TE_V1_NOTIF_* (whom to notify when) */ struct iwm_time_event_cmd_v1 { /* COMMON_INDEX_HDR_API_S_VER_1 */ uint32_t id_and_color; uint32_t action; uint32_t id; /* IWM_MAC_TIME_EVENT_DATA_API_S_VER_1 */ uint32_t apply_time; uint32_t max_delay; uint32_t dep_policy; uint32_t depends_on; uint32_t is_present; uint32_t max_frags; uint32_t interval; uint32_t interval_reciprocal; uint32_t duration; uint32_t repeat; uint32_t notify; } __packed; /* IWM_MAC_TIME_EVENT_CMD_API_S_VER_1 */ /* Time event - defines for command API v2 */ /* * @IWM_TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed. * @IWM_TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only * the first fragment is scheduled. * @IWM_TE_V2_FRAG_DUAL: fragmentation of the time event is allowed, but only * the first 2 fragments are scheduled. * @IWM_TE_V2_FRAG_ENDLESS: fragmentation of the time event is allowed, and any * number of fragments are valid. * * Other than the constant defined above, specifying a fragmentation value 'x' * means that the event can be fragmented but only the first 'x' will be * scheduled. */ enum { IWM_TE_V2_FRAG_NONE = 0, IWM_TE_V2_FRAG_SINGLE = 1, IWM_TE_V2_FRAG_DUAL = 2, IWM_TE_V2_FRAG_MAX = 0xfe, IWM_TE_V2_FRAG_ENDLESS = 0xff }; /* Repeat the time event endlessly (until removed) */ #define IWM_TE_V2_REPEAT_ENDLESS 0xff /* If a Time Event has bounded repetitions, this is the maximal value */ #define IWM_TE_V2_REPEAT_MAX 0xfe #define IWM_TE_V2_PLACEMENT_POS 12 #define IWM_TE_V2_ABSENCE_POS 15 /* Time event policy values (for time event cmd api v2) * A notification (both event and fragment) includes a status indicating weather * the FW was able to schedule the event or not. For fragment start/end * notification the status is always success. There is no start/end fragment * notification for monolithic events. * * @IWM_TE_V2_DEFAULT_POLICY: independent, social, present, unoticable * @IWM_TE_V2_NOTIF_HOST_EVENT_START: request/receive notification on event start * @IWM_TE_V2_NOTIF_HOST_EVENT_END:request/receive notification on event end * @IWM_TE_V2_NOTIF_INTERNAL_EVENT_START: internal FW use * @IWM_TE_V2_NOTIF_INTERNAL_EVENT_END: internal FW use. * @IWM_TE_V2_NOTIF_HOST_FRAG_START: request/receive notification on frag start * @IWM_TE_V2_NOTIF_HOST_FRAG_END:request/receive notification on frag end * @IWM_TE_V2_NOTIF_INTERNAL_FRAG_START: internal FW use. * @IWM_TE_V2_NOTIF_INTERNAL_FRAG_END: internal FW use. * @IWM_TE_V2_DEP_OTHER: depends on another time event * @IWM_TE_V2_DEP_TSF: depends on a specific time * @IWM_TE_V2_EVENT_SOCIOPATHIC: can't co-exist with other events of tha same MAC * @IWM_TE_V2_ABSENCE: are we present or absent during the Time Event. */ enum { IWM_TE_V2_DEFAULT_POLICY = 0x0, /* notifications (event start/stop, fragment start/stop) */ IWM_TE_V2_NOTIF_HOST_EVENT_START = (1 << 0), IWM_TE_V2_NOTIF_HOST_EVENT_END = (1 << 1), IWM_TE_V2_NOTIF_INTERNAL_EVENT_START = (1 << 2), IWM_TE_V2_NOTIF_INTERNAL_EVENT_END = (1 << 3), IWM_TE_V2_NOTIF_HOST_FRAG_START = (1 << 4), IWM_TE_V2_NOTIF_HOST_FRAG_END = (1 << 5), IWM_TE_V2_NOTIF_INTERNAL_FRAG_START = (1 << 6), IWM_TE_V2_NOTIF_INTERNAL_FRAG_END = (1 << 7), IWM_TE_V2_NOTIF_MSK = 0xff, /* placement characteristics */ IWM_TE_V2_DEP_OTHER = (1 << IWM_TE_V2_PLACEMENT_POS), IWM_TE_V2_DEP_TSF = (1 << (IWM_TE_V2_PLACEMENT_POS + 1)), IWM_TE_V2_EVENT_SOCIOPATHIC = (1 << (IWM_TE_V2_PLACEMENT_POS + 2)), /* are we present or absent during the Time Event. */ IWM_TE_V2_ABSENCE = (1 << IWM_TE_V2_ABSENCE_POS), }; /** * struct iwm_time_event_cmd_api_v2 - configuring Time Events * with struct IWM_MAC_TIME_EVENT_DATA_API_S_VER_2 (see also * with version 1. determined by IWM_UCODE_TLV_FLAGS) * ( IWM_TIME_EVENT_CMD = 0x29 ) * @id_and_color: ID and color of the relevant MAC * @action: action to perform, one of IWM_FW_CTXT_ACTION_* * @id: this field has two meanings, depending on the action: * If the action is ADD, then it means the type of event to add. * For all other actions it is the unique event ID assigned when the * event was added by the FW. * @apply_time: When to start the Time Event (in GP2) * @max_delay: maximum delay to event's start (apply time), in TU * @depends_on: the unique ID of the event we depend on (if any) * @interval: interval between repetitions, in TU * @duration: duration of event in TU * @repeat: how many repetitions to do, can be IWM_TE_REPEAT_ENDLESS * @max_frags: maximal number of fragments the Time Event can be divided to * @policy: defines whether uCode shall notify the host or other uCode modules * on event and/or fragment start and/or end * using one of IWM_TE_INDEPENDENT, IWM_TE_DEP_OTHER, IWM_TE_DEP_TSF * IWM_TE_EVENT_SOCIOPATHIC * using IWM_TE_ABSENCE and using IWM_TE_NOTIF_* */ struct iwm_time_event_cmd_v2 { /* COMMON_INDEX_HDR_API_S_VER_1 */ uint32_t id_and_color; uint32_t action; uint32_t id; /* IWM_MAC_TIME_EVENT_DATA_API_S_VER_2 */ uint32_t apply_time; uint32_t max_delay; uint32_t depends_on; uint32_t interval; uint32_t duration; uint8_t repeat; uint8_t max_frags; uint16_t policy; } __packed; /* IWM_MAC_TIME_EVENT_CMD_API_S_VER_2 */ /** * struct iwm_time_event_resp - response structure to iwm_time_event_cmd * @status: bit 0 indicates success, all others specify errors * @id: the Time Event type * @unique_id: the unique ID assigned (in ADD) or given (others) to the TE * @id_and_color: ID and color of the relevant MAC */ struct iwm_time_event_resp { uint32_t status; uint32_t id; uint32_t unique_id; uint32_t id_and_color; } __packed; /* IWM_MAC_TIME_EVENT_RSP_API_S_VER_1 */ /** * struct iwm_time_event_notif - notifications of time event start/stop * ( IWM_TIME_EVENT_NOTIFICATION = 0x2a ) * @timestamp: action timestamp in GP2 * @session_id: session's unique id * @unique_id: unique id of the Time Event itself * @id_and_color: ID and color of the relevant MAC * @action: one of IWM_TE_NOTIF_START or IWM_TE_NOTIF_END * @status: true if scheduled, false otherwise (not executed) */ struct iwm_time_event_notif { uint32_t timestamp; uint32_t session_id; uint32_t unique_id; uint32_t id_and_color; uint32_t action; uint32_t status; } __packed; /* IWM_MAC_TIME_EVENT_NTFY_API_S_VER_1 */ /* Bindings and Time Quota */ /** * struct iwm_binding_cmd - configuring bindings * ( IWM_BINDING_CONTEXT_CMD = 0x2b ) * @id_and_color: ID and color of the relevant Binding * @action: action to perform, one of IWM_FW_CTXT_ACTION_* * @macs: array of MAC id and colors which belong to the binding * @phy: PHY id and color which belongs to the binding */ struct iwm_binding_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 */ uint32_t id_and_color; uint32_t action; /* IWM_BINDING_DATA_API_S_VER_1 */ uint32_t macs[IWM_MAX_MACS_IN_BINDING]; uint32_t phy; } __packed; /* IWM_BINDING_CMD_API_S_VER_1 */ /* The maximal number of fragments in the FW's schedule session */ #define IWM_MVM_MAX_QUOTA 128 /** * struct iwm_time_quota_data - configuration of time quota per binding * @id_and_color: ID and color of the relevant Binding * @quota: absolute time quota in TU. The scheduler will try to divide the * remainig quota (after Time Events) according to this quota. * @max_duration: max uninterrupted context duration in TU */ struct iwm_time_quota_data { uint32_t id_and_color; uint32_t quota; uint32_t max_duration; } __packed; /* IWM_TIME_QUOTA_DATA_API_S_VER_1 */ /** * struct iwm_time_quota_cmd - configuration of time quota between bindings * ( IWM_TIME_QUOTA_CMD = 0x2c ) * @quotas: allocations per binding */ struct iwm_time_quota_cmd { struct iwm_time_quota_data quotas[IWM_MAX_BINDINGS]; } __packed; /* IWM_TIME_QUOTA_ALLOCATION_CMD_API_S_VER_1 */ /* PHY context */ /* Supported bands */ #define IWM_PHY_BAND_5 (0) #define IWM_PHY_BAND_24 (1) /* Supported channel width, vary if there is VHT support */ #define IWM_PHY_VHT_CHANNEL_MODE20 (0x0) #define IWM_PHY_VHT_CHANNEL_MODE40 (0x1) #define IWM_PHY_VHT_CHANNEL_MODE80 (0x2) #define IWM_PHY_VHT_CHANNEL_MODE160 (0x3) /* * Control channel position: * For legacy set bit means upper channel, otherwise lower. * For VHT - bit-2 marks if the control is lower/upper relative to center-freq * bits-1:0 mark the distance from the center freq. for 20Mhz, offset is 0. * center_freq * | * 40Mhz |_______|_______| * 80Mhz |_______|_______|_______|_______| * 160Mhz |_______|_______|_______|_______|_______|_______|_______|_______| * code 011 010 001 000 | 100 101 110 111 */ #define IWM_PHY_VHT_CTRL_POS_1_BELOW (0x0) #define IWM_PHY_VHT_CTRL_POS_2_BELOW (0x1) #define IWM_PHY_VHT_CTRL_POS_3_BELOW (0x2) #define IWM_PHY_VHT_CTRL_POS_4_BELOW (0x3) #define IWM_PHY_VHT_CTRL_POS_1_ABOVE (0x4) #define IWM_PHY_VHT_CTRL_POS_2_ABOVE (0x5) #define IWM_PHY_VHT_CTRL_POS_3_ABOVE (0x6) #define IWM_PHY_VHT_CTRL_POS_4_ABOVE (0x7) /* * @band: IWM_PHY_BAND_* * @channel: channel number * @width: PHY_[VHT|LEGACY]_CHANNEL_* * @ctrl channel: PHY_[VHT|LEGACY]_CTRL_* */ struct iwm_fw_channel_info { uint8_t band; uint8_t channel; uint8_t width; uint8_t ctrl_pos; } __packed; #define IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS (0) #define IWM_PHY_RX_CHAIN_DRIVER_FORCE_MSK \ (0x1 << IWM_PHY_RX_CHAIN_DRIVER_FORCE_POS) #define IWM_PHY_RX_CHAIN_VALID_POS (1) #define IWM_PHY_RX_CHAIN_VALID_MSK \ (0x7 << IWM_PHY_RX_CHAIN_VALID_POS) #define IWM_PHY_RX_CHAIN_FORCE_SEL_POS (4) #define IWM_PHY_RX_CHAIN_FORCE_SEL_MSK \ (0x7 << IWM_PHY_RX_CHAIN_FORCE_SEL_POS) #define IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS (7) #define IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_MSK \ (0x7 << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS) #define IWM_PHY_RX_CHAIN_CNT_POS (10) #define IWM_PHY_RX_CHAIN_CNT_MSK \ (0x3 << IWM_PHY_RX_CHAIN_CNT_POS) #define IWM_PHY_RX_CHAIN_MIMO_CNT_POS (12) #define IWM_PHY_RX_CHAIN_MIMO_CNT_MSK \ (0x3 << IWM_PHY_RX_CHAIN_MIMO_CNT_POS) #define IWM_PHY_RX_CHAIN_MIMO_FORCE_POS (14) #define IWM_PHY_RX_CHAIN_MIMO_FORCE_MSK \ (0x1 << IWM_PHY_RX_CHAIN_MIMO_FORCE_POS) /* TODO: fix the value, make it depend on firmware at runtime? */ #define IWM_NUM_PHY_CTX 3 /* TODO: complete missing documentation */ /** * struct iwm_phy_context_cmd - config of the PHY context * ( IWM_PHY_CONTEXT_CMD = 0x8 ) * @id_and_color: ID and color of the relevant Binding * @action: action to perform, one of IWM_FW_CTXT_ACTION_* * @apply_time: 0 means immediate apply and context switch. * other value means apply new params after X usecs * @tx_param_color: ??? * @channel_info: * @txchain_info: ??? * @rxchain_info: ??? * @acquisition_data: ??? * @dsp_cfg_flags: set to 0 */ struct iwm_phy_context_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 */ uint32_t id_and_color; uint32_t action; /* IWM_PHY_CONTEXT_DATA_API_S_VER_1 */ uint32_t apply_time; uint32_t tx_param_color; struct iwm_fw_channel_info ci; uint32_t txchain_info; uint32_t rxchain_info; uint32_t acquisition_data; uint32_t dsp_cfg_flags; } __packed; /* IWM_PHY_CONTEXT_CMD_API_VER_1 */ #define IWM_RX_INFO_PHY_CNT 8 #define IWM_RX_INFO_ENERGY_ANT_ABC_IDX 1 #define IWM_RX_INFO_ENERGY_ANT_A_MSK 0x000000ff #define IWM_RX_INFO_ENERGY_ANT_B_MSK 0x0000ff00 #define IWM_RX_INFO_ENERGY_ANT_C_MSK 0x00ff0000 #define IWM_RX_INFO_ENERGY_ANT_A_POS 0 #define IWM_RX_INFO_ENERGY_ANT_B_POS 8 #define IWM_RX_INFO_ENERGY_ANT_C_POS 16 #define IWM_RX_INFO_AGC_IDX 1 #define IWM_RX_INFO_RSSI_AB_IDX 2 #define IWM_OFDM_AGC_A_MSK 0x0000007f #define IWM_OFDM_AGC_A_POS 0 #define IWM_OFDM_AGC_B_MSK 0x00003f80 #define IWM_OFDM_AGC_B_POS 7 #define IWM_OFDM_AGC_CODE_MSK 0x3fe00000 #define IWM_OFDM_AGC_CODE_POS 20 #define IWM_OFDM_RSSI_INBAND_A_MSK 0x00ff #define IWM_OFDM_RSSI_A_POS 0 #define IWM_OFDM_RSSI_ALLBAND_A_MSK 0xff00 #define IWM_OFDM_RSSI_ALLBAND_A_POS 8 #define IWM_OFDM_RSSI_INBAND_B_MSK 0xff0000 #define IWM_OFDM_RSSI_B_POS 16 #define IWM_OFDM_RSSI_ALLBAND_B_MSK 0xff000000 #define IWM_OFDM_RSSI_ALLBAND_B_POS 24 /** * struct iwm_rx_phy_info - phy info * (IWM_REPLY_RX_PHY_CMD = 0xc0) * @non_cfg_phy_cnt: non configurable DSP phy data byte count * @cfg_phy_cnt: configurable DSP phy data byte count * @stat_id: configurable DSP phy data set ID * @reserved1: * @system_timestamp: GP2 at on air rise * @timestamp: TSF at on air rise * @beacon_time_stamp: beacon at on-air rise * @phy_flags: general phy flags: band, modulation, ... * @channel: channel number * @non_cfg_phy_buf: for various implementations of non_cfg_phy * @rate_n_flags: IWM_RATE_MCS_* * @byte_count: frame's byte-count * @frame_time: frame's time on the air, based on byte count and frame rate * calculation * @mac_active_msk: what MACs were active when the frame was received * * Before each Rx, the device sends this data. It contains PHY information * about the reception of the packet. */ struct iwm_rx_phy_info { uint8_t non_cfg_phy_cnt; uint8_t cfg_phy_cnt; uint8_t stat_id; uint8_t reserved1; uint32_t system_timestamp; uint64_t timestamp; uint32_t beacon_time_stamp; uint16_t phy_flags; #define IWM_PHY_INFO_FLAG_SHPREAMBLE (1 << 2) uint16_t channel; uint32_t non_cfg_phy[IWM_RX_INFO_PHY_CNT]; uint8_t rate; uint8_t rflags; uint16_t xrflags; uint32_t byte_count; uint16_t mac_active_msk; uint16_t frame_time; } __packed; struct iwm_rx_mpdu_res_start { uint16_t byte_count; uint16_t reserved; } __packed; /** * enum iwm_rx_phy_flags - to parse %iwm_rx_phy_info phy_flags * @IWM_RX_RES_PHY_FLAGS_BAND_24: true if the packet was received on 2.4 band * @IWM_RX_RES_PHY_FLAGS_MOD_CCK: * @IWM_RX_RES_PHY_FLAGS_SHORT_PREAMBLE: true if packet's preamble was short * @IWM_RX_RES_PHY_FLAGS_NARROW_BAND: * @IWM_RX_RES_PHY_FLAGS_ANTENNA: antenna on which the packet was received * @IWM_RX_RES_PHY_FLAGS_AGG: set if the packet was part of an A-MPDU * @IWM_RX_RES_PHY_FLAGS_OFDM_HT: The frame was an HT frame * @IWM_RX_RES_PHY_FLAGS_OFDM_GF: The frame used GF preamble * @IWM_RX_RES_PHY_FLAGS_OFDM_VHT: The frame was a VHT frame */ enum iwm_rx_phy_flags { IWM_RX_RES_PHY_FLAGS_BAND_24 = (1 << 0), IWM_RX_RES_PHY_FLAGS_MOD_CCK = (1 << 1), IWM_RX_RES_PHY_FLAGS_SHORT_PREAMBLE = (1 << 2), IWM_RX_RES_PHY_FLAGS_NARROW_BAND = (1 << 3), IWM_RX_RES_PHY_FLAGS_ANTENNA = (0x7 << 4), IWM_RX_RES_PHY_FLAGS_ANTENNA_POS = 4, IWM_RX_RES_PHY_FLAGS_AGG = (1 << 7), IWM_RX_RES_PHY_FLAGS_OFDM_HT = (1 << 8), IWM_RX_RES_PHY_FLAGS_OFDM_GF = (1 << 9), IWM_RX_RES_PHY_FLAGS_OFDM_VHT = (1 << 10), }; /** * enum iwm_mvm_rx_status - written by fw for each Rx packet * @IWM_RX_MPDU_RES_STATUS_CRC_OK: CRC is fine * @IWM_RX_MPDU_RES_STATUS_OVERRUN_OK: there was no RXE overflow * @IWM_RX_MPDU_RES_STATUS_SRC_STA_FOUND: * @IWM_RX_MPDU_RES_STATUS_KEY_VALID: * @IWM_RX_MPDU_RES_STATUS_KEY_PARAM_OK: * @IWM_RX_MPDU_RES_STATUS_ICV_OK: ICV is fine, if not, the packet is destroyed * @IWM_RX_MPDU_RES_STATUS_MIC_OK: used for CCM alg only. TKIP MIC is checked * in the driver. * @IWM_RX_MPDU_RES_STATUS_TTAK_OK: TTAK is fine * @IWM_RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR: valid for alg = CCM_CMAC or * alg = CCM only. Checks replay attack for 11w frames. Relevant only if * %IWM_RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME is set. * @IWM_RX_MPDU_RES_STATUS_SEC_NO_ENC: this frame is not encrypted * @IWM_RX_MPDU_RES_STATUS_SEC_WEP_ENC: this frame is encrypted using WEP * @IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC: this frame is encrypted using CCM * @IWM_RX_MPDU_RES_STATUS_SEC_TKIP_ENC: this frame is encrypted using TKIP * @IWM_RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC: this frame is encrypted using CCM_CMAC * @IWM_RX_MPDU_RES_STATUS_SEC_ENC_ERR: this frame couldn't be decrypted * @IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK: bitmask of the encryption algorithm * @IWM_RX_MPDU_RES_STATUS_DEC_DONE: this frame has been successfully decrypted * @IWM_RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP: * @IWM_RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP: * @IWM_RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT: * @IWM_RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME: this frame is an 11w management frame * @IWM_RX_MPDU_RES_STATUS_HASH_INDEX_MSK: * @IWM_RX_MPDU_RES_STATUS_STA_ID_MSK: * @IWM_RX_MPDU_RES_STATUS_RRF_KILL: * @IWM_RX_MPDU_RES_STATUS_FILTERING_MSK: * @IWM_RX_MPDU_RES_STATUS2_FILTERING_MSK: */ enum iwm_mvm_rx_status { IWM_RX_MPDU_RES_STATUS_CRC_OK = (1 << 0), IWM_RX_MPDU_RES_STATUS_OVERRUN_OK = (1 << 1), IWM_RX_MPDU_RES_STATUS_SRC_STA_FOUND = (1 << 2), IWM_RX_MPDU_RES_STATUS_KEY_VALID = (1 << 3), IWM_RX_MPDU_RES_STATUS_KEY_PARAM_OK = (1 << 4), IWM_RX_MPDU_RES_STATUS_ICV_OK = (1 << 5), IWM_RX_MPDU_RES_STATUS_MIC_OK = (1 << 6), IWM_RX_MPDU_RES_STATUS_TTAK_OK = (1 << 7), IWM_RX_MPDU_RES_STATUS_MNG_FRAME_REPLAY_ERR = (1 << 7), IWM_RX_MPDU_RES_STATUS_SEC_NO_ENC = (0 << 8), IWM_RX_MPDU_RES_STATUS_SEC_WEP_ENC = (1 << 8), IWM_RX_MPDU_RES_STATUS_SEC_CCM_ENC = (2 << 8), IWM_RX_MPDU_RES_STATUS_SEC_TKIP_ENC = (3 << 8), IWM_RX_MPDU_RES_STATUS_SEC_EXT_ENC = (4 << 8), IWM_RX_MPDU_RES_STATUS_SEC_CCM_CMAC_ENC = (6 << 8), IWM_RX_MPDU_RES_STATUS_SEC_ENC_ERR = (7 << 8), IWM_RX_MPDU_RES_STATUS_SEC_ENC_MSK = (7 << 8), IWM_RX_MPDU_RES_STATUS_DEC_DONE = (1 << 11), IWM_RX_MPDU_RES_STATUS_PROTECT_FRAME_BIT_CMP = (1 << 12), IWM_RX_MPDU_RES_STATUS_EXT_IV_BIT_CMP = (1 << 13), IWM_RX_MPDU_RES_STATUS_KEY_ID_CMP_BIT = (1 << 14), IWM_RX_MPDU_RES_STATUS_ROBUST_MNG_FRAME = (1 << 15), IWM_RX_MPDU_RES_STATUS_HASH_INDEX_MSK = (0x3F0000), IWM_RX_MPDU_RES_STATUS_STA_ID_MSK = (0x1f000000), IWM_RX_MPDU_RES_STATUS_RRF_KILL = (1 << 29), IWM_RX_MPDU_RES_STATUS_FILTERING_MSK = (0xc00000), IWM_RX_MPDU_RES_STATUS2_FILTERING_MSK = (0xc0000000), }; /** * struct iwm_radio_version_notif - information on the radio version * ( IWM_RADIO_VERSION_NOTIFICATION = 0x68 ) * @radio_flavor: * @radio_step: * @radio_dash: */ struct iwm_radio_version_notif { uint32_t radio_flavor; uint32_t radio_step; uint32_t radio_dash; } __packed; /* IWM_RADIO_VERSION_NOTOFICATION_S_VER_1 */ enum iwm_card_state_flags { IWM_CARD_ENABLED = 0x00, IWM_HW_CARD_DISABLED = 0x01, IWM_SW_CARD_DISABLED = 0x02, IWM_CT_KILL_CARD_DISABLED = 0x04, IWM_HALT_CARD_DISABLED = 0x08, IWM_CARD_DISABLED_MSK = 0x0f, IWM_CARD_IS_RX_ON = 0x10, }; /** * struct iwm_radio_version_notif - information on the radio version * (IWM_CARD_STATE_NOTIFICATION = 0xa1 ) * @flags: %iwm_card_state_flags */ struct iwm_card_state_notif { uint32_t flags; } __packed; /* CARD_STATE_NTFY_API_S_VER_1 */ /** * struct iwm_missed_beacons_notif - information on missed beacons * ( IWM_MISSED_BEACONS_NOTIFICATION = 0xa2 ) * @mac_id: interface ID * @consec_missed_beacons_since_last_rx: number of consecutive missed * beacons since last RX. * @consec_missed_beacons: number of consecutive missed beacons * @num_expected_beacons: * @num_recvd_beacons: */ struct iwm_missed_beacons_notif { uint32_t mac_id; uint32_t consec_missed_beacons_since_last_rx; uint32_t consec_missed_beacons; uint32_t num_expected_beacons; uint32_t num_recvd_beacons; } __packed; /* IWM_MISSED_BEACON_NTFY_API_S_VER_3 */ /** + * struct iwm_mfuart_load_notif - mfuart image version & status + * ( IWM_MFUART_LOAD_NOTIFICATION = 0xb1 ) + * @installed_ver: installed image version + * @external_ver: external image version + * @status: MFUART loading status + * @duration: MFUART loading time +*/ +struct iwm_mfuart_load_notif { + uint32_t installed_ver; + uint32_t external_ver; + uint32_t status; + uint32_t duration; +} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/ + +/** * struct iwm_set_calib_default_cmd - set default value for calibration. * ( IWM_SET_CALIB_DEFAULT_CMD = 0x8e ) * @calib_index: the calibration to set value for * @length: of data * @data: the value to set for the calibration result */ struct iwm_set_calib_default_cmd { uint16_t calib_index; uint16_t length; uint8_t data[0]; } __packed; /* IWM_PHY_CALIB_OVERRIDE_VALUES_S */ #define IWM_MAX_PORT_ID_NUM 2 #define IWM_MAX_MCAST_FILTERING_ADDRESSES 256 /** * struct iwm_mcast_filter_cmd - configure multicast filter. * @filter_own: Set 1 to filter out multicast packets sent by station itself * @port_id: Multicast MAC addresses array specifier. This is a strange way * to identify network interface adopted in host-device IF. * It is used by FW as index in array of addresses. This array has * IWM_MAX_PORT_ID_NUM members. * @count: Number of MAC addresses in the array * @pass_all: Set 1 to pass all multicast packets. * @bssid: current association BSSID. * @addr_list: Place holder for array of MAC addresses. * IMPORTANT: add padding if necessary to ensure DWORD alignment. */ struct iwm_mcast_filter_cmd { uint8_t filter_own; uint8_t port_id; uint8_t count; uint8_t pass_all; uint8_t bssid[6]; uint8_t reserved[2]; uint8_t addr_list[0]; } __packed; /* IWM_MCAST_FILTERING_CMD_API_S_VER_1 */ struct iwm_mvm_statistics_dbg { uint32_t burst_check; uint32_t burst_count; uint32_t wait_for_silence_timeout_cnt; uint32_t reserved[3]; } __packed; /* IWM_STATISTICS_DEBUG_API_S_VER_2 */ struct iwm_mvm_statistics_div { uint32_t tx_on_a; uint32_t tx_on_b; uint32_t exec_time; uint32_t probe_time; uint32_t rssi_ant; uint32_t reserved2; } __packed; /* IWM_STATISTICS_SLOW_DIV_API_S_VER_2 */ struct iwm_mvm_statistics_general_common { uint32_t temperature; /* radio temperature */ uint32_t temperature_m; /* radio voltage */ struct iwm_mvm_statistics_dbg dbg; uint32_t sleep_time; uint32_t slots_out; uint32_t slots_idle; uint32_t ttl_timestamp; struct iwm_mvm_statistics_div div; uint32_t rx_enable_counter; /* * num_of_sos_states: * count the number of times we have to re-tune * in order to get out of bad PHY status */ uint32_t num_of_sos_states; } __packed; /* IWM_STATISTICS_GENERAL_API_S_VER_5 */ struct iwm_mvm_statistics_rx_non_phy { uint32_t bogus_cts; /* CTS received when not expecting CTS */ uint32_t bogus_ack; /* ACK received when not expecting ACK */ uint32_t non_bssid_frames; /* number of frames with BSSID that * doesn't belong to the STA BSSID */ uint32_t filtered_frames; /* count frames that were dumped in the * filtering process */ uint32_t non_channel_beacons; /* beacons with our bss id but not on * our serving channel */ uint32_t channel_beacons; /* beacons with our bss id and in our * serving channel */ uint32_t num_missed_bcon; /* number of missed beacons */ uint32_t adc_rx_saturation_time; /* count in 0.8us units the time the * ADC was in saturation */ uint32_t ina_detection_search_time;/* total time (in 0.8us) searched * for INA */ uint32_t beacon_silence_rssi[3];/* RSSI silence after beacon frame */ uint32_t interference_data_flag; /* flag for interference data * availability. 1 when data is * available. */ uint32_t channel_load; /* counts RX Enable time in uSec */ uint32_t dsp_false_alarms; /* DSP false alarm (both OFDM * and CCK) counter */ uint32_t beacon_rssi_a; uint32_t beacon_rssi_b; uint32_t beacon_rssi_c; uint32_t beacon_energy_a; uint32_t beacon_energy_b; uint32_t beacon_energy_c; uint32_t num_bt_kills; uint32_t mac_id; uint32_t directed_data_mpdu; } __packed; /* IWM_STATISTICS_RX_NON_PHY_API_S_VER_3 */ struct iwm_mvm_statistics_rx_phy { uint32_t ina_cnt; uint32_t fina_cnt; uint32_t plcp_err; uint32_t crc32_err; uint32_t overrun_err; uint32_t early_overrun_err; uint32_t crc32_good; uint32_t false_alarm_cnt; uint32_t fina_sync_err_cnt; uint32_t sfd_timeout; uint32_t fina_timeout; uint32_t unresponded_rts; uint32_t rxe_frame_limit_overrun; uint32_t sent_ack_cnt; uint32_t sent_cts_cnt; uint32_t sent_ba_rsp_cnt; uint32_t dsp_self_kill; uint32_t mh_format_err; uint32_t re_acq_main_rssi_sum; uint32_t reserved; } __packed; /* IWM_STATISTICS_RX_PHY_API_S_VER_2 */ struct iwm_mvm_statistics_rx_ht_phy { uint32_t plcp_err; uint32_t overrun_err; uint32_t early_overrun_err; uint32_t crc32_good; uint32_t crc32_err; uint32_t mh_format_err; uint32_t agg_crc32_good; uint32_t agg_mpdu_cnt; uint32_t agg_cnt; uint32_t unsupport_mcs; } __packed; /* IWM_STATISTICS_HT_RX_PHY_API_S_VER_1 */ #define IWM_MAX_CHAINS 3 struct iwm_mvm_statistics_tx_non_phy_agg { uint32_t ba_timeout; uint32_t ba_reschedule_frames; uint32_t scd_query_agg_frame_cnt; uint32_t scd_query_no_agg; uint32_t scd_query_agg; uint32_t scd_query_mismatch; uint32_t frame_not_ready; uint32_t underrun; uint32_t bt_prio_kill; uint32_t rx_ba_rsp_cnt; int8_t txpower[IWM_MAX_CHAINS]; int8_t reserved; uint32_t reserved2; } __packed; /* IWM_STATISTICS_TX_NON_PHY_AGG_API_S_VER_1 */ struct iwm_mvm_statistics_tx_channel_width { uint32_t ext_cca_narrow_ch20[1]; uint32_t ext_cca_narrow_ch40[2]; uint32_t ext_cca_narrow_ch80[3]; uint32_t ext_cca_narrow_ch160[4]; uint32_t last_tx_ch_width_indx; uint32_t rx_detected_per_ch_width[4]; uint32_t success_per_ch_width[4]; uint32_t fail_per_ch_width[4]; }; /* IWM_STATISTICS_TX_CHANNEL_WIDTH_API_S_VER_1 */ struct iwm_mvm_statistics_tx { uint32_t preamble_cnt; uint32_t rx_detected_cnt; uint32_t bt_prio_defer_cnt; uint32_t bt_prio_kill_cnt; uint32_t few_bytes_cnt; uint32_t cts_timeout; uint32_t ack_timeout; uint32_t expected_ack_cnt; uint32_t actual_ack_cnt; uint32_t dump_msdu_cnt; uint32_t burst_abort_next_frame_mismatch_cnt; uint32_t burst_abort_missing_next_frame_cnt; uint32_t cts_timeout_collision; uint32_t ack_or_ba_timeout_collision; struct iwm_mvm_statistics_tx_non_phy_agg agg; struct iwm_mvm_statistics_tx_channel_width channel_width; } __packed; /* IWM_STATISTICS_TX_API_S_VER_4 */ struct iwm_mvm_statistics_bt_activity { uint32_t hi_priority_tx_req_cnt; uint32_t hi_priority_tx_denied_cnt; uint32_t lo_priority_tx_req_cnt; uint32_t lo_priority_tx_denied_cnt; uint32_t hi_priority_rx_req_cnt; uint32_t hi_priority_rx_denied_cnt; uint32_t lo_priority_rx_req_cnt; uint32_t lo_priority_rx_denied_cnt; } __packed; /* IWM_STATISTICS_BT_ACTIVITY_API_S_VER_1 */ struct iwm_mvm_statistics_general { struct iwm_mvm_statistics_general_common common; uint32_t beacon_filtered; uint32_t missed_beacons; int8_t beacon_filter_average_energy; int8_t beacon_filter_reason; int8_t beacon_filter_current_energy; int8_t beacon_filter_reserved; uint32_t beacon_filter_delta_time; struct iwm_mvm_statistics_bt_activity bt_activity; } __packed; /* IWM_STATISTICS_GENERAL_API_S_VER_5 */ struct iwm_mvm_statistics_rx { struct iwm_mvm_statistics_rx_phy ofdm; struct iwm_mvm_statistics_rx_phy cck; struct iwm_mvm_statistics_rx_non_phy general; struct iwm_mvm_statistics_rx_ht_phy ofdm_ht; } __packed; /* IWM_STATISTICS_RX_API_S_VER_3 */ /* * IWM_STATISTICS_NOTIFICATION = 0x9d (notification only, not a command) * * By default, uCode issues this notification after receiving a beacon * while associated. To disable this behavior, set DISABLE_NOTIF flag in the * IWM_REPLY_STATISTICS_CMD 0x9c, above. * * Statistics counters continue to increment beacon after beacon, but are * cleared when changing channels or when driver issues IWM_REPLY_STATISTICS_CMD * 0x9c with CLEAR_STATS bit set (see above). * * uCode also issues this notification during scans. uCode clears statistics * appropriately so that each notification contains statistics for only the * one channel that has just been scanned. */ struct iwm_notif_statistics { /* IWM_STATISTICS_NTFY_API_S_VER_8 */ uint32_t flag; struct iwm_mvm_statistics_rx rx; struct iwm_mvm_statistics_tx tx; struct iwm_mvm_statistics_general general; } __packed; /*********************************** * Smart Fifo API ***********************************/ /* Smart Fifo state */ enum iwm_sf_state { IWM_SF_LONG_DELAY_ON = 0, /* should never be called by driver */ IWM_SF_FULL_ON, IWM_SF_UNINIT, IWM_SF_INIT_OFF, IWM_SF_HW_NUM_STATES }; /* Smart Fifo possible scenario */ enum iwm_sf_scenario { IWM_SF_SCENARIO_SINGLE_UNICAST, IWM_SF_SCENARIO_AGG_UNICAST, IWM_SF_SCENARIO_MULTICAST, IWM_SF_SCENARIO_BA_RESP, IWM_SF_SCENARIO_TX_RESP, IWM_SF_NUM_SCENARIO }; #define IWM_SF_TRANSIENT_STATES_NUMBER 2 /* IWM_SF_LONG_DELAY_ON and IWM_SF_FULL_ON */ #define IWM_SF_NUM_TIMEOUT_TYPES 2 /* Aging timer and Idle timer */ /* smart FIFO default values */ #define IWM_SF_W_MARK_SISO 4096 #define IWM_SF_W_MARK_MIMO2 8192 #define IWM_SF_W_MARK_MIMO3 6144 #define IWM_SF_W_MARK_LEGACY 4096 #define IWM_SF_W_MARK_SCAN 4096 +/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ +#define IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ +#define IWM_SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ + /* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */ #define IWM_SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ #define IWM_SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ #define IWM_SF_AGG_UNICAST_IDLE_TIMER 320 /* 300 uSec */ #define IWM_SF_AGG_UNICAST_AGING_TIMER 2016 /* 2 mSec */ #define IWM_SF_MCAST_IDLE_TIMER 2016 /* 2 mSec */ #define IWM_SF_MCAST_AGING_TIMER 10016 /* 10 mSec */ #define IWM_SF_BA_IDLE_TIMER 320 /* 300 uSec */ #define IWM_SF_BA_AGING_TIMER 2016 /* 2 mSec */ #define IWM_SF_TX_RE_IDLE_TIMER 320 /* 300 uSec */ #define IWM_SF_TX_RE_AGING_TIMER 2016 /* 2 mSec */ #define IWM_SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ +#define IWM_SF_CFG_DUMMY_NOTIF_OFF (1 << 16) + /** * Smart Fifo configuration command. * @state: smart fifo state, types listed in iwm_sf_sate. * @watermark: Minimum allowed available free space in RXF for transient state. * @long_delay_timeouts: aging and idle timer values for each scenario * in long delay state. * @full_on_timeouts: timer values for each scenario in full on state. */ struct iwm_sf_cfg_cmd { enum iwm_sf_state state; uint32_t watermark[IWM_SF_TRANSIENT_STATES_NUMBER]; uint32_t long_delay_timeouts[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES]; uint32_t full_on_timeouts[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES]; } __packed; /* IWM_SF_CFG_API_S_VER_2 */ /* * END mvm/fw-api.h */ /* * BEGIN mvm/fw-api-mac.h */ /* * The first MAC indices (starting from 0) * are available to the driver, AUX follows */ #define IWM_MAC_INDEX_AUX 4 #define IWM_MAC_INDEX_MIN_DRIVER 0 #define IWM_NUM_MAC_INDEX_DRIVER IWM_MAC_INDEX_AUX enum iwm_ac { IWM_AC_BK, IWM_AC_BE, IWM_AC_VI, IWM_AC_VO, IWM_AC_NUM, }; /** * enum iwm_mac_protection_flags - MAC context flags * @IWM_MAC_PROT_FLG_TGG_PROTECT: 11g protection when transmitting OFDM frames, * this will require CCK RTS/CTS2self. * RTS/CTS will protect full burst time. * @IWM_MAC_PROT_FLG_HT_PROT: enable HT protection * @IWM_MAC_PROT_FLG_FAT_PROT: protect 40 MHz transmissions * @IWM_MAC_PROT_FLG_SELF_CTS_EN: allow CTS2self */ enum iwm_mac_protection_flags { IWM_MAC_PROT_FLG_TGG_PROTECT = (1 << 3), IWM_MAC_PROT_FLG_HT_PROT = (1 << 23), IWM_MAC_PROT_FLG_FAT_PROT = (1 << 24), IWM_MAC_PROT_FLG_SELF_CTS_EN = (1 << 30), }; #define IWM_MAC_FLG_SHORT_SLOT (1 << 4) #define IWM_MAC_FLG_SHORT_PREAMBLE (1 << 5) /** * enum iwm_mac_types - Supported MAC types * @IWM_FW_MAC_TYPE_FIRST: lowest supported MAC type * @IWM_FW_MAC_TYPE_AUX: Auxiliary MAC (internal) * @IWM_FW_MAC_TYPE_LISTENER: monitor MAC type (?) * @IWM_FW_MAC_TYPE_PIBSS: Pseudo-IBSS * @IWM_FW_MAC_TYPE_IBSS: IBSS * @IWM_FW_MAC_TYPE_BSS_STA: BSS (managed) station * @IWM_FW_MAC_TYPE_P2P_DEVICE: P2P Device * @IWM_FW_MAC_TYPE_P2P_STA: P2P client * @IWM_FW_MAC_TYPE_GO: P2P GO * @IWM_FW_MAC_TYPE_TEST: ? * @IWM_FW_MAC_TYPE_MAX: highest support MAC type */ enum iwm_mac_types { IWM_FW_MAC_TYPE_FIRST = 1, IWM_FW_MAC_TYPE_AUX = IWM_FW_MAC_TYPE_FIRST, IWM_FW_MAC_TYPE_LISTENER, IWM_FW_MAC_TYPE_PIBSS, IWM_FW_MAC_TYPE_IBSS, IWM_FW_MAC_TYPE_BSS_STA, IWM_FW_MAC_TYPE_P2P_DEVICE, IWM_FW_MAC_TYPE_P2P_STA, IWM_FW_MAC_TYPE_GO, IWM_FW_MAC_TYPE_TEST, IWM_FW_MAC_TYPE_MAX = IWM_FW_MAC_TYPE_TEST }; /* IWM_MAC_CONTEXT_TYPE_API_E_VER_1 */ /** * enum iwm_tsf_id - TSF hw timer ID * @IWM_TSF_ID_A: use TSF A * @IWM_TSF_ID_B: use TSF B * @IWM_TSF_ID_C: use TSF C * @IWM_TSF_ID_D: use TSF D * @IWM_NUM_TSF_IDS: number of TSF timers available */ enum iwm_tsf_id { IWM_TSF_ID_A = 0, IWM_TSF_ID_B = 1, IWM_TSF_ID_C = 2, IWM_TSF_ID_D = 3, IWM_NUM_TSF_IDS = 4, }; /* IWM_TSF_ID_API_E_VER_1 */ /** * struct iwm_mac_data_ap - configuration data for AP MAC context * @beacon_time: beacon transmit time in system time * @beacon_tsf: beacon transmit time in TSF * @bi: beacon interval in TU * @bi_reciprocal: 2^32 / bi * @dtim_interval: dtim transmit time in TU * @dtim_reciprocal: 2^32 / dtim_interval * @mcast_qid: queue ID for multicast traffic * @beacon_template: beacon template ID */ struct iwm_mac_data_ap { uint32_t beacon_time; uint64_t beacon_tsf; uint32_t bi; uint32_t bi_reciprocal; uint32_t dtim_interval; uint32_t dtim_reciprocal; uint32_t mcast_qid; uint32_t beacon_template; } __packed; /* AP_MAC_DATA_API_S_VER_1 */ /** * struct iwm_mac_data_ibss - configuration data for IBSS MAC context * @beacon_time: beacon transmit time in system time * @beacon_tsf: beacon transmit time in TSF * @bi: beacon interval in TU * @bi_reciprocal: 2^32 / bi * @beacon_template: beacon template ID */ struct iwm_mac_data_ibss { uint32_t beacon_time; uint64_t beacon_tsf; uint32_t bi; uint32_t bi_reciprocal; uint32_t beacon_template; } __packed; /* IBSS_MAC_DATA_API_S_VER_1 */ /** * struct iwm_mac_data_sta - configuration data for station MAC context * @is_assoc: 1 for associated state, 0 otherwise * @dtim_time: DTIM arrival time in system time * @dtim_tsf: DTIM arrival time in TSF * @bi: beacon interval in TU, applicable only when associated * @bi_reciprocal: 2^32 / bi , applicable only when associated * @dtim_interval: DTIM interval in TU, applicable only when associated * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated * @listen_interval: in beacon intervals, applicable only when associated * @assoc_id: unique ID assigned by the AP during association */ struct iwm_mac_data_sta { uint32_t is_assoc; uint32_t dtim_time; uint64_t dtim_tsf; uint32_t bi; uint32_t bi_reciprocal; uint32_t dtim_interval; uint32_t dtim_reciprocal; uint32_t listen_interval; uint32_t assoc_id; uint32_t assoc_beacon_arrive_time; } __packed; /* IWM_STA_MAC_DATA_API_S_VER_1 */ /** * struct iwm_mac_data_go - configuration data for P2P GO MAC context * @ap: iwm_mac_data_ap struct with most config data * @ctwin: client traffic window in TU (period after TBTT when GO is present). * 0 indicates that there is no CT window. * @opp_ps_enabled: indicate that opportunistic PS allowed */ struct iwm_mac_data_go { struct iwm_mac_data_ap ap; uint32_t ctwin; uint32_t opp_ps_enabled; } __packed; /* GO_MAC_DATA_API_S_VER_1 */ /** * struct iwm_mac_data_p2p_sta - configuration data for P2P client MAC context * @sta: iwm_mac_data_sta struct with most config data * @ctwin: client traffic window in TU (period after TBTT when GO is present). * 0 indicates that there is no CT window. */ struct iwm_mac_data_p2p_sta { struct iwm_mac_data_sta sta; uint32_t ctwin; } __packed; /* P2P_STA_MAC_DATA_API_S_VER_1 */ /** * struct iwm_mac_data_pibss - Pseudo IBSS config data * @stats_interval: interval in TU between statistics notifications to host. */ struct iwm_mac_data_pibss { uint32_t stats_interval; } __packed; /* PIBSS_MAC_DATA_API_S_VER_1 */ /* * struct iwm_mac_data_p2p_dev - configuration data for the P2P Device MAC * context. * @is_disc_extended: if set to true, P2P Device discoverability is enabled on * other channels as well. This should be to true only in case that the * device is discoverable and there is an active GO. Note that setting this * field when not needed, will increase the number of interrupts and have * effect on the platform power, as this setting opens the Rx filters on * all macs. */ struct iwm_mac_data_p2p_dev { uint32_t is_disc_extended; } __packed; /* _P2P_DEV_MAC_DATA_API_S_VER_1 */ /** * enum iwm_mac_filter_flags - MAC context filter flags * @IWM_MAC_FILTER_IN_PROMISC: accept all data frames * @IWM_MAC_FILTER_IN_CONTROL_AND_MGMT: pass all mangement and * control frames to the host * @IWM_MAC_FILTER_ACCEPT_GRP: accept multicast frames * @IWM_MAC_FILTER_DIS_DECRYPT: don't decrypt unicast frames * @IWM_MAC_FILTER_DIS_GRP_DECRYPT: don't decrypt multicast frames * @IWM_MAC_FILTER_IN_BEACON: transfer foreign BSS's beacons to host * (in station mode when associated) * @IWM_MAC_FILTER_OUT_BCAST: filter out all broadcast frames * @IWM_MAC_FILTER_IN_CRC32: extract FCS and append it to frames * @IWM_MAC_FILTER_IN_PROBE_REQUEST: pass probe requests to host */ enum iwm_mac_filter_flags { IWM_MAC_FILTER_IN_PROMISC = (1 << 0), IWM_MAC_FILTER_IN_CONTROL_AND_MGMT = (1 << 1), IWM_MAC_FILTER_ACCEPT_GRP = (1 << 2), IWM_MAC_FILTER_DIS_DECRYPT = (1 << 3), IWM_MAC_FILTER_DIS_GRP_DECRYPT = (1 << 4), IWM_MAC_FILTER_IN_BEACON = (1 << 6), IWM_MAC_FILTER_OUT_BCAST = (1 << 8), IWM_MAC_FILTER_IN_CRC32 = (1 << 11), IWM_MAC_FILTER_IN_PROBE_REQUEST = (1 << 12), }; /** * enum iwm_mac_qos_flags - QoS flags * @IWM_MAC_QOS_FLG_UPDATE_EDCA: ? * @IWM_MAC_QOS_FLG_TGN: HT is enabled * @IWM_MAC_QOS_FLG_TXOP_TYPE: ? * */ enum iwm_mac_qos_flags { IWM_MAC_QOS_FLG_UPDATE_EDCA = (1 << 0), IWM_MAC_QOS_FLG_TGN = (1 << 1), IWM_MAC_QOS_FLG_TXOP_TYPE = (1 << 4), }; /** * struct iwm_ac_qos - QOS timing params for IWM_MAC_CONTEXT_CMD * @cw_min: Contention window, start value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x0f. * @cw_max: Contention window, max value in numbers of slots. * Should be a power-of-2, minus 1. Device's default is 0x3f. * @aifsn: Number of slots in Arbitration Interframe Space (before * performing random backoff timing prior to Tx). Device default 1. * @fifos_mask: FIFOs used by this MAC for this AC * @edca_txop: Length of Tx opportunity, in uSecs. Device default is 0. * * One instance of this config struct for each of 4 EDCA access categories * in struct iwm_qosparam_cmd. * * Device will automatically increase contention window by (2*CW) + 1 for each * transmission retry. Device uses cw_max as a bit mask, ANDed with new CW * value, to cap the CW value. */ struct iwm_ac_qos { uint16_t cw_min; uint16_t cw_max; uint8_t aifsn; uint8_t fifos_mask; uint16_t edca_txop; } __packed; /* IWM_AC_QOS_API_S_VER_2 */ /** * struct iwm_mac_ctx_cmd - command structure to configure MAC contexts * ( IWM_MAC_CONTEXT_CMD = 0x28 ) * @id_and_color: ID and color of the MAC * @action: action to perform, one of IWM_FW_CTXT_ACTION_* * @mac_type: one of IWM_FW_MAC_TYPE_* * @tsd_id: TSF HW timer, one of IWM_TSF_ID_* * @node_addr: MAC address * @bssid_addr: BSSID * @cck_rates: basic rates available for CCK * @ofdm_rates: basic rates available for OFDM * @protection_flags: combination of IWM_MAC_PROT_FLG_FLAG_* * @cck_short_preamble: 0x20 for enabling short preamble, 0 otherwise * @short_slot: 0x10 for enabling short slots, 0 otherwise * @filter_flags: combination of IWM_MAC_FILTER_* * @qos_flags: from IWM_MAC_QOS_FLG_* * @ac: one iwm_mac_qos configuration for each AC * @mac_specific: one of struct iwm_mac_data_*, according to mac_type */ struct iwm_mac_ctx_cmd { /* COMMON_INDEX_HDR_API_S_VER_1 */ uint32_t id_and_color; uint32_t action; /* IWM_MAC_CONTEXT_COMMON_DATA_API_S_VER_1 */ uint32_t mac_type; uint32_t tsf_id; uint8_t node_addr[6]; uint16_t reserved_for_node_addr; uint8_t bssid_addr[6]; uint16_t reserved_for_bssid_addr; uint32_t cck_rates; uint32_t ofdm_rates; uint32_t protection_flags; uint32_t cck_short_preamble; uint32_t short_slot; uint32_t filter_flags; /* IWM_MAC_QOS_PARAM_API_S_VER_1 */ uint32_t qos_flags; struct iwm_ac_qos ac[IWM_AC_NUM+1]; /* IWM_MAC_CONTEXT_COMMON_DATA_API_S */ union { struct iwm_mac_data_ap ap; struct iwm_mac_data_go go; struct iwm_mac_data_sta sta; struct iwm_mac_data_p2p_sta p2p_sta; struct iwm_mac_data_p2p_dev p2p_dev; struct iwm_mac_data_pibss pibss; struct iwm_mac_data_ibss ibss; }; } __packed; /* IWM_MAC_CONTEXT_CMD_API_S_VER_1 */ static inline uint32_t iwm_mvm_reciprocal(uint32_t v) { if (!v) return 0; return 0xFFFFFFFF / v; } #define IWM_NONQOS_SEQ_GET 0x1 #define IWM_NONQOS_SEQ_SET 0x2 struct iwm_nonqos_seq_query_cmd { uint32_t get_set_flag; uint32_t mac_id_n_color; uint16_t value; uint16_t reserved; } __packed; /* IWM_NON_QOS_TX_COUNTER_GET_SET_API_S_VER_1 */ /* * END mvm/fw-api-mac.h */ /* * BEGIN mvm/fw-api-power.h */ /* Power Management Commands, Responses, Notifications */ /* Radio LP RX Energy Threshold measured in dBm */ #define IWM_POWER_LPRX_RSSI_THRESHOLD 75 #define IWM_POWER_LPRX_RSSI_THRESHOLD_MAX 94 #define IWM_POWER_LPRX_RSSI_THRESHOLD_MIN 30 /** * enum iwm_scan_flags - masks for power table command flags * @IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off * receiver and transmitter. '0' - does not allow. * @IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK: '0' Driver disables power management, * '1' Driver enables PM (use rest of parameters) * @IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK: '0' PM have to walk up every DTIM, * '1' PM could sleep over DTIM till listen Interval. * @IWM_POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all * access categories are both delivery and trigger enabled. * @IWM_POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and * PBW Snoozing enabled * @IWM_POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask * @IWM_POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. * @IWM_POWER_FLAGS_AP_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving * detection enablement */ enum iwm_power_flags { IWM_POWER_FLAGS_POWER_SAVE_ENA_MSK = (1 << 0), IWM_POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK = (1 << 1), IWM_POWER_FLAGS_SKIP_OVER_DTIM_MSK = (1 << 2), IWM_POWER_FLAGS_SNOOZE_ENA_MSK = (1 << 5), IWM_POWER_FLAGS_BT_SCO_ENA = (1 << 8), IWM_POWER_FLAGS_ADVANCE_PM_ENA_MSK = (1 << 9), IWM_POWER_FLAGS_LPRX_ENA_MSK = (1 << 11), IWM_POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = (1 << 12), }; #define IWM_POWER_VEC_SIZE 5 /** * struct iwm_powertable_cmd - legacy power command. Beside old API support this * is used also with a new power API for device wide power settings. * IWM_POWER_TABLE_CMD = 0x77 (command, has simple generic response) * * @flags: Power table command flags from IWM_POWER_FLAGS_* * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. * Minimum allowed:- 3 * DTIM. Keep alive period must be * set regardless of power scheme or current power state. * FW use this value also when PM is disabled. * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to * PSM transition - legacy PM * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag * is set. For example, if it is required to skip over * one DTIM, this value need to be set to 2 (DTIM periods). * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm */ struct iwm_powertable_cmd { /* PM_POWER_TABLE_CMD_API_S_VER_6 */ uint16_t flags; uint8_t keep_alive_seconds; uint8_t debug_flags; uint32_t rx_data_timeout; uint32_t tx_data_timeout; uint32_t sleep_interval[IWM_POWER_VEC_SIZE]; uint32_t skip_dtim_periods; uint32_t lprx_rssi_threshold; } __packed; /** * enum iwm_device_power_flags - masks for device power command flags * @DEVIC_POWER_FLAGS_POWER_SAVE_ENA_MSK: '1' Allow to save power by turning off * receiver and transmitter. '0' - does not allow. This flag should be * always set to '1' unless one need to disable actual power down for debug * purposes. * @IWM_DEVICE_POWER_FLAGS_CAM_MSK: '1' CAM (Continuous Active Mode) is set, meaning * that power management is disabled. '0' Power management is enabled, one * of power schemes is applied. */ enum iwm_device_power_flags { IWM_DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK = (1 << 0), IWM_DEVICE_POWER_FLAGS_CAM_MSK = (1 << 13), }; /** * struct iwm_device_power_cmd - device wide power command. * IWM_DEVICE_POWER_CMD = 0x77 (command, has simple generic response) * * @flags: Power table command flags from IWM_DEVICE_POWER_FLAGS_* */ struct iwm_device_power_cmd { /* PM_POWER_TABLE_CMD_API_S_VER_6 */ uint16_t flags; uint16_t reserved; } __packed; /** * struct iwm_mac_power_cmd - New power command containing uAPSD support * IWM_MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response) * @id_and_color: MAC contex identifier * @flags: Power table command flags from POWER_FLAGS_* * @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec. * Minimum allowed:- 3 * DTIM. Keep alive period must be * set regardless of power scheme or current power state. * FW use this value also when PM is disabled. * @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to * PSM transition - legacy PM * @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to * PSM transition - legacy PM * @sleep_interval: not in use * @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag * is set. For example, if it is required to skip over * one DTIM, this value need to be set to 2 (DTIM periods). * @rx_data_timeout_uapsd: Minimum time (usec) from last Rx packet for AM to * PSM transition - uAPSD * @tx_data_timeout_uapsd: Minimum time (usec) from last Tx packet for AM to * PSM transition - uAPSD * @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled. * Default: 80dbm * @num_skip_dtim: Number of DTIMs to skip if Skip over DTIM flag is set * @snooze_interval: Maximum time between attempts to retrieve buffered data * from the AP [msec] * @snooze_window: A window of time in which PBW snoozing insures that all * packets received. It is also the minimum time from last * received unicast RX packet, before client stops snoozing * for data. [msec] * @snooze_step: TBD * @qndp_tid: TID client shall use for uAPSD QNDP triggers * @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for * each corresponding AC. * Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values. * @uapsd_max_sp: Use IEEE80211_WMM_IE_STA_QOSINFO_SP_* for correct * values. * @heavy_tx_thld_packets: TX threshold measured in number of packets * @heavy_rx_thld_packets: RX threshold measured in number of packets * @heavy_tx_thld_percentage: TX threshold measured in load's percentage * @heavy_rx_thld_percentage: RX threshold measured in load's percentage * @limited_ps_threshold: */ struct iwm_mac_power_cmd { /* CONTEXT_DESC_API_T_VER_1 */ uint32_t id_and_color; /* CLIENT_PM_POWER_TABLE_S_VER_1 */ uint16_t flags; uint16_t keep_alive_seconds; uint32_t rx_data_timeout; uint32_t tx_data_timeout; uint32_t rx_data_timeout_uapsd; uint32_t tx_data_timeout_uapsd; uint8_t lprx_rssi_threshold; uint8_t skip_dtim_periods; uint16_t snooze_interval; uint16_t snooze_window; uint8_t snooze_step; uint8_t qndp_tid; uint8_t uapsd_ac_flags; uint8_t uapsd_max_sp; uint8_t heavy_tx_thld_packets; uint8_t heavy_rx_thld_packets; uint8_t heavy_tx_thld_percentage; uint8_t heavy_rx_thld_percentage; uint8_t limited_ps_threshold; uint8_t reserved; } __packed; /* * struct iwm_uapsd_misbehaving_ap_notif - FW sends this notification when * associated AP is identified as improperly implementing uAPSD protocol. * IWM_PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78 * @sta_id: index of station in uCode's station table - associated AP ID in * this context. */ struct iwm_uapsd_misbehaving_ap_notif { uint32_t sta_id; uint8_t mac_id; uint8_t reserved[3]; } __packed; /** * struct iwm_beacon_filter_cmd * IWM_REPLY_BEACON_FILTERING_CMD = 0xd2 (command) * @id_and_color: MAC contex identifier * @bf_energy_delta: Used for RSSI filtering, if in 'normal' state. Send beacon * to driver if delta in Energy values calculated for this and last * passed beacon is greater than this threshold. Zero value means that * the Energy change is ignored for beacon filtering, and beacon will * not be forced to be sent to driver regardless of this delta. Typical * energy delta 5dB. * @bf_roaming_energy_delta: Used for RSSI filtering, if in 'roaming' state. * Send beacon to driver if delta in Energy values calculated for this * and last passed beacon is greater than this threshold. Zero value * means that the Energy change is ignored for beacon filtering while in * Roaming state, typical energy delta 1dB. * @bf_roaming_state: Used for RSSI filtering. If absolute Energy values * calculated for current beacon is less than the threshold, use * Roaming Energy Delta Threshold, otherwise use normal Energy Delta * Threshold. Typical energy threshold is -72dBm. * @bf_temp_threshold: This threshold determines the type of temperature * filtering (Slow or Fast) that is selected (Units are in Celsuis): * If the current temperature is above this threshold - Fast filter * will be used, If the current temperature is below this threshold - * Slow filter will be used. * @bf_temp_fast_filter: Send Beacon to driver if delta in temperature values * calculated for this and the last passed beacon is greater than this * threshold. Zero value means that the temperature change is ignored for * beacon filtering; beacons will not be forced to be sent to driver * regardless of whether its temperature has been changed. * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values * calculated for this and the last passed beacon is greater than this * threshold. Zero value means that the temperature change is ignored for * beacon filtering; beacons will not be forced to be sent to driver * regardless of whether its temperature has been changed. * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. * @bf_filter_escape_timer: Send beacons to to driver if no beacons were passed * for a specific period of time. Units: Beacons. * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed * for a longer period of time then this escape-timeout. Units: Beacons. * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. */ struct iwm_beacon_filter_cmd { uint32_t bf_energy_delta; uint32_t bf_roaming_energy_delta; uint32_t bf_roaming_state; uint32_t bf_temp_threshold; uint32_t bf_temp_fast_filter; uint32_t bf_temp_slow_filter; uint32_t bf_enable_beacon_filter; uint32_t bf_debug_flag; uint32_t bf_escape_timer; uint32_t ba_escape_timer; uint32_t ba_enable_beacon_abort; } __packed; /* Beacon filtering and beacon abort */ #define IWM_BF_ENERGY_DELTA_DEFAULT 5 #define IWM_BF_ENERGY_DELTA_MAX 255 #define IWM_BF_ENERGY_DELTA_MIN 0 #define IWM_BF_ROAMING_ENERGY_DELTA_DEFAULT 1 #define IWM_BF_ROAMING_ENERGY_DELTA_MAX 255 #define IWM_BF_ROAMING_ENERGY_DELTA_MIN 0 #define IWM_BF_ROAMING_STATE_DEFAULT 72 #define IWM_BF_ROAMING_STATE_MAX 255 #define IWM_BF_ROAMING_STATE_MIN 0 #define IWM_BF_TEMP_THRESHOLD_DEFAULT 112 #define IWM_BF_TEMP_THRESHOLD_MAX 255 #define IWM_BF_TEMP_THRESHOLD_MIN 0 #define IWM_BF_TEMP_FAST_FILTER_DEFAULT 1 #define IWM_BF_TEMP_FAST_FILTER_MAX 255 #define IWM_BF_TEMP_FAST_FILTER_MIN 0 #define IWM_BF_TEMP_SLOW_FILTER_DEFAULT 5 #define IWM_BF_TEMP_SLOW_FILTER_MAX 255 #define IWM_BF_TEMP_SLOW_FILTER_MIN 0 #define IWM_BF_ENABLE_BEACON_FILTER_DEFAULT 1 #define IWM_BF_DEBUG_FLAG_DEFAULT 0 #define IWM_BF_ESCAPE_TIMER_DEFAULT 50 #define IWM_BF_ESCAPE_TIMER_MAX 1024 #define IWM_BF_ESCAPE_TIMER_MIN 0 #define IWM_BA_ESCAPE_TIMER_DEFAULT 6 #define IWM_BA_ESCAPE_TIMER_D3 9 #define IWM_BA_ESCAPE_TIMER_MAX 1024 #define IWM_BA_ESCAPE_TIMER_MIN 0 #define IWM_BA_ENABLE_BEACON_ABORT_DEFAULT 1 #define IWM_BF_CMD_CONFIG_DEFAULTS \ .bf_energy_delta = htole32(IWM_BF_ENERGY_DELTA_DEFAULT), \ .bf_roaming_energy_delta = \ htole32(IWM_BF_ROAMING_ENERGY_DELTA_DEFAULT), \ .bf_roaming_state = htole32(IWM_BF_ROAMING_STATE_DEFAULT), \ .bf_temp_threshold = htole32(IWM_BF_TEMP_THRESHOLD_DEFAULT), \ .bf_temp_fast_filter = htole32(IWM_BF_TEMP_FAST_FILTER_DEFAULT), \ .bf_temp_slow_filter = htole32(IWM_BF_TEMP_SLOW_FILTER_DEFAULT), \ .bf_debug_flag = htole32(IWM_BF_DEBUG_FLAG_DEFAULT), \ .bf_escape_timer = htole32(IWM_BF_ESCAPE_TIMER_DEFAULT), \ .ba_escape_timer = htole32(IWM_BA_ESCAPE_TIMER_DEFAULT) /* * END mvm/fw-api-power.h */ /* * BEGIN mvm/fw-api-rs.h */ /* * These serve as indexes into * struct iwm_rate_info fw_rate_idx_to_plcp[IWM_RATE_COUNT]; * TODO: avoid overlap between legacy and HT rates */ enum { IWM_RATE_1M_INDEX = 0, IWM_FIRST_CCK_RATE = IWM_RATE_1M_INDEX, IWM_RATE_2M_INDEX, IWM_RATE_5M_INDEX, IWM_RATE_11M_INDEX, IWM_LAST_CCK_RATE = IWM_RATE_11M_INDEX, IWM_RATE_6M_INDEX, IWM_FIRST_OFDM_RATE = IWM_RATE_6M_INDEX, IWM_RATE_MCS_0_INDEX = IWM_RATE_6M_INDEX, IWM_FIRST_HT_RATE = IWM_RATE_MCS_0_INDEX, IWM_FIRST_VHT_RATE = IWM_RATE_MCS_0_INDEX, IWM_RATE_9M_INDEX, IWM_RATE_12M_INDEX, IWM_RATE_MCS_1_INDEX = IWM_RATE_12M_INDEX, IWM_RATE_18M_INDEX, IWM_RATE_MCS_2_INDEX = IWM_RATE_18M_INDEX, IWM_RATE_24M_INDEX, IWM_RATE_MCS_3_INDEX = IWM_RATE_24M_INDEX, IWM_RATE_36M_INDEX, IWM_RATE_MCS_4_INDEX = IWM_RATE_36M_INDEX, IWM_RATE_48M_INDEX, IWM_RATE_MCS_5_INDEX = IWM_RATE_48M_INDEX, IWM_RATE_54M_INDEX, IWM_RATE_MCS_6_INDEX = IWM_RATE_54M_INDEX, IWM_LAST_NON_HT_RATE = IWM_RATE_54M_INDEX, IWM_RATE_60M_INDEX, IWM_RATE_MCS_7_INDEX = IWM_RATE_60M_INDEX, IWM_LAST_HT_RATE = IWM_RATE_MCS_7_INDEX, IWM_RATE_MCS_8_INDEX, IWM_RATE_MCS_9_INDEX, IWM_LAST_VHT_RATE = IWM_RATE_MCS_9_INDEX, IWM_RATE_COUNT_LEGACY = IWM_LAST_NON_HT_RATE + 1, IWM_RATE_COUNT = IWM_LAST_VHT_RATE + 1, }; #define IWM_RATE_BIT_MSK(r) (1 << (IWM_RATE_##r##M_INDEX)) /* fw API values for legacy bit rates, both OFDM and CCK */ enum { IWM_RATE_6M_PLCP = 13, IWM_RATE_9M_PLCP = 15, IWM_RATE_12M_PLCP = 5, IWM_RATE_18M_PLCP = 7, IWM_RATE_24M_PLCP = 9, IWM_RATE_36M_PLCP = 11, IWM_RATE_48M_PLCP = 1, IWM_RATE_54M_PLCP = 3, IWM_RATE_1M_PLCP = 10, IWM_RATE_2M_PLCP = 20, IWM_RATE_5M_PLCP = 55, IWM_RATE_11M_PLCP = 110, IWM_RATE_INVM_PLCP = -1, }; /* * rate_n_flags bit fields * * The 32-bit value has different layouts in the low 8 bites depending on the * format. There are three formats, HT, VHT and legacy (11abg, with subformats * for CCK and OFDM). * * High-throughput (HT) rate format * bit 8 is 1, bit 26 is 0, bit 9 is 0 (OFDM) * Very High-throughput (VHT) rate format * bit 8 is 0, bit 26 is 1, bit 9 is 0 (OFDM) * Legacy OFDM rate format for bits 7:0 * bit 8 is 0, bit 26 is 0, bit 9 is 0 (OFDM) * Legacy CCK rate format for bits 7:0: * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK) */ /* Bit 8: (1) HT format, (0) legacy or VHT format */ #define IWM_RATE_MCS_HT_POS 8 #define IWM_RATE_MCS_HT_MSK (1 << IWM_RATE_MCS_HT_POS) /* Bit 9: (1) CCK, (0) OFDM. HT (bit 8) must be "0" for this bit to be valid */ #define IWM_RATE_MCS_CCK_POS 9 #define IWM_RATE_MCS_CCK_MSK (1 << IWM_RATE_MCS_CCK_POS) /* Bit 26: (1) VHT format, (0) legacy format in bits 8:0 */ #define IWM_RATE_MCS_VHT_POS 26 #define IWM_RATE_MCS_VHT_MSK (1 << IWM_RATE_MCS_VHT_POS) /* * High-throughput (HT) rate format for bits 7:0 * * 2-0: MCS rate base * 0) 6 Mbps * 1) 12 Mbps * 2) 18 Mbps * 3) 24 Mbps * 4) 36 Mbps * 5) 48 Mbps * 6) 54 Mbps * 7) 60 Mbps * 4-3: 0) Single stream (SISO) * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) * 5: Value of 0x20 in bits 7:0 indicates 6 Mbps HT40 duplicate data * (bits 7-6 are zero) * * Together the low 5 bits work out to the MCS index because we don't * support MCSes above 15/23, and 0-7 have one stream, 8-15 have two * streams and 16-23 have three streams. We could also support MCS 32 * which is the duplicate 20 MHz MCS (bit 5 set, all others zero.) */ #define IWM_RATE_HT_MCS_RATE_CODE_MSK 0x7 #define IWM_RATE_HT_MCS_NSS_POS 3 #define IWM_RATE_HT_MCS_NSS_MSK (3 << IWM_RATE_HT_MCS_NSS_POS) /* Bit 10: (1) Use Green Field preamble */ #define IWM_RATE_HT_MCS_GF_POS 10 #define IWM_RATE_HT_MCS_GF_MSK (1 << IWM_RATE_HT_MCS_GF_POS) #define IWM_RATE_HT_MCS_INDEX_MSK 0x3f /* * Very High-throughput (VHT) rate format for bits 7:0 * * 3-0: VHT MCS (0-9) * 5-4: number of streams - 1: * 0) Single stream (SISO) * 1) Dual stream (MIMO) * 2) Triple stream (MIMO) */ /* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ #define IWM_RATE_VHT_MCS_RATE_CODE_MSK 0xf #define IWM_RATE_VHT_MCS_NSS_POS 4 #define IWM_RATE_VHT_MCS_NSS_MSK (3 << IWM_RATE_VHT_MCS_NSS_POS) /* * Legacy OFDM rate format for bits 7:0 * * 3-0: 0xD) 6 Mbps * 0xF) 9 Mbps * 0x5) 12 Mbps * 0x7) 18 Mbps * 0x9) 24 Mbps * 0xB) 36 Mbps * 0x1) 48 Mbps * 0x3) 54 Mbps * (bits 7-4 are 0) * * Legacy CCK rate format for bits 7:0: * bit 8 is 0, bit 26 is 0, bit 9 is 1 (CCK): * * 6-0: 10) 1 Mbps * 20) 2 Mbps * 55) 5.5 Mbps * 110) 11 Mbps * (bit 7 is 0) */ #define IWM_RATE_LEGACY_RATE_MSK 0xff /* * Bit 11-12: (0) 20MHz, (1) 40MHz, (2) 80MHz, (3) 160MHz * 0 and 1 are valid for HT and VHT, 2 and 3 only for VHT */ #define IWM_RATE_MCS_CHAN_WIDTH_POS 11 #define IWM_RATE_MCS_CHAN_WIDTH_MSK (3 << IWM_RATE_MCS_CHAN_WIDTH_POS) #define IWM_RATE_MCS_CHAN_WIDTH_20 (0 << IWM_RATE_MCS_CHAN_WIDTH_POS) #define IWM_RATE_MCS_CHAN_WIDTH_40 (1 << IWM_RATE_MCS_CHAN_WIDTH_POS) #define IWM_RATE_MCS_CHAN_WIDTH_80 (2 << IWM_RATE_MCS_CHAN_WIDTH_POS) #define IWM_RATE_MCS_CHAN_WIDTH_160 (3 << IWM_RATE_MCS_CHAN_WIDTH_POS) /* Bit 13: (1) Short guard interval (0.4 usec), (0) normal GI (0.8 usec) */ #define IWM_RATE_MCS_SGI_POS 13 #define IWM_RATE_MCS_SGI_MSK (1 << IWM_RATE_MCS_SGI_POS) /* Bit 14-16: Antenna selection (1) Ant A, (2) Ant B, (4) Ant C */ #define IWM_RATE_MCS_ANT_POS 14 #define IWM_RATE_MCS_ANT_A_MSK (1 << IWM_RATE_MCS_ANT_POS) #define IWM_RATE_MCS_ANT_B_MSK (2 << IWM_RATE_MCS_ANT_POS) #define IWM_RATE_MCS_ANT_C_MSK (4 << IWM_RATE_MCS_ANT_POS) #define IWM_RATE_MCS_ANT_AB_MSK (IWM_RATE_MCS_ANT_A_MSK | \ IWM_RATE_MCS_ANT_B_MSK) #define IWM_RATE_MCS_ANT_ABC_MSK (IWM_RATE_MCS_ANT_AB_MSK | \ IWM_RATE_MCS_ANT_C_MSK) #define IWM_RATE_MCS_ANT_MSK IWM_RATE_MCS_ANT_ABC_MSK #define IWM_RATE_MCS_ANT_NUM 3 /* Bit 17-18: (0) SS, (1) SS*2 */ #define IWM_RATE_MCS_STBC_POS 17 #define IWM_RATE_MCS_STBC_MSK (1 << IWM_RATE_MCS_STBC_POS) /* Bit 19: (0) Beamforming is off, (1) Beamforming is on */ #define IWM_RATE_MCS_BF_POS 19 #define IWM_RATE_MCS_BF_MSK (1 << IWM_RATE_MCS_BF_POS) /* Bit 20: (0) ZLF is off, (1) ZLF is on */ #define IWM_RATE_MCS_ZLF_POS 20 #define IWM_RATE_MCS_ZLF_MSK (1 << IWM_RATE_MCS_ZLF_POS) /* Bit 24-25: (0) 20MHz (no dup), (1) 2x20MHz, (2) 4x20MHz, 3 8x20MHz */ #define IWM_RATE_MCS_DUP_POS 24 #define IWM_RATE_MCS_DUP_MSK (3 << IWM_RATE_MCS_DUP_POS) /* Bit 27: (1) LDPC enabled, (0) LDPC disabled */ #define IWM_RATE_MCS_LDPC_POS 27 #define IWM_RATE_MCS_LDPC_MSK (1 << IWM_RATE_MCS_LDPC_POS) /* Link Quality definitions */ /* # entries in rate scale table to support Tx retries */ #define IWM_LQ_MAX_RETRY_NUM 16 /* Link quality command flags bit fields */ /* Bit 0: (0) Don't use RTS (1) Use RTS */ #define IWM_LQ_FLAG_USE_RTS_POS 0 #define IWM_LQ_FLAG_USE_RTS_MSK (1 << IWM_LQ_FLAG_USE_RTS_POS) /* Bit 1-3: LQ command color. Used to match responses to LQ commands */ #define IWM_LQ_FLAG_COLOR_POS 1 #define IWM_LQ_FLAG_COLOR_MSK (7 << IWM_LQ_FLAG_COLOR_POS) /* Bit 4-5: Tx RTS BW Signalling * (0) No RTS BW signalling * (1) Static BW signalling * (2) Dynamic BW signalling */ #define IWM_LQ_FLAG_RTS_BW_SIG_POS 4 #define IWM_LQ_FLAG_RTS_BW_SIG_NONE (0 << IWM_LQ_FLAG_RTS_BW_SIG_POS) #define IWM_LQ_FLAG_RTS_BW_SIG_STATIC (1 << IWM_LQ_FLAG_RTS_BW_SIG_POS) #define IWM_LQ_FLAG_RTS_BW_SIG_DYNAMIC (2 << IWM_LQ_FLAG_RTS_BW_SIG_POS) /* Bit 6: (0) No dynamic BW selection (1) Allow dynamic BW selection * Dyanmic BW selection allows Tx with narrower BW then requested in rates */ #define IWM_LQ_FLAG_DYNAMIC_BW_POS 6 #define IWM_LQ_FLAG_DYNAMIC_BW_MSK (1 << IWM_LQ_FLAG_DYNAMIC_BW_POS) /** * struct iwm_lq_cmd - link quality command * @sta_id: station to update * @control: not used * @flags: combination of IWM_LQ_FLAG_* * @mimo_delim: the first SISO index in rs_table, which separates MIMO * and SISO rates * @single_stream_ant_msk: best antenna for SISO (can be dual in CDD). * Should be ANT_[ABC] * @dual_stream_ant_msk: best antennas for MIMO, combination of ANT_[ABC] * @initial_rate_index: first index from rs_table per AC category * @agg_time_limit: aggregation max time threshold in usec/100, meaning * value of 100 is one usec. Range is 100 to 8000 * @agg_disable_start_th: try-count threshold for starting aggregation. * If a frame has higher try-count, it should not be selected for * starting an aggregation sequence. * @agg_frame_cnt_limit: max frame count in an aggregation. * 0: no limit * 1: no aggregation (one frame per aggregation) * 2 - 0x3f: maximal number of frames (up to 3f == 63) * @rs_table: array of rates for each TX try, each is rate_n_flags, * meaning it is a combination of IWM_RATE_MCS_* and IWM_RATE_*_PLCP * @bf_params: beam forming params, currently not used */ struct iwm_lq_cmd { uint8_t sta_id; uint8_t reserved1; uint16_t control; /* LINK_QUAL_GENERAL_PARAMS_API_S_VER_1 */ uint8_t flags; uint8_t mimo_delim; uint8_t single_stream_ant_msk; uint8_t dual_stream_ant_msk; uint8_t initial_rate_index[IWM_AC_NUM]; /* LINK_QUAL_AGG_PARAMS_API_S_VER_1 */ uint16_t agg_time_limit; uint8_t agg_disable_start_th; uint8_t agg_frame_cnt_limit; uint32_t reserved2; uint32_t rs_table[IWM_LQ_MAX_RETRY_NUM]; uint32_t bf_params; }; /* LINK_QUALITY_CMD_API_S_VER_1 */ /* * END mvm/fw-api-rs.h */ /* * BEGIN mvm/fw-api-tx.h */ /** * enum iwm_tx_flags - bitmasks for tx_flags in TX command * @IWM_TX_CMD_FLG_PROT_REQUIRE: use RTS or CTS-to-self to protect the frame * @IWM_TX_CMD_FLG_ACK: expect ACK from receiving station * @IWM_TX_CMD_FLG_STA_RATE: use RS table with initial index from the TX command. * Otherwise, use rate_n_flags from the TX command * @IWM_TX_CMD_FLG_BA: this frame is a block ack * @IWM_TX_CMD_FLG_BAR: this frame is a BA request, immediate BAR is expected * Must set IWM_TX_CMD_FLG_ACK with this flag. * @IWM_TX_CMD_FLG_TXOP_PROT: protect frame with full TXOP protection * @IWM_TX_CMD_FLG_VHT_NDPA: mark frame is NDPA for VHT beamformer sequence * @IWM_TX_CMD_FLG_HT_NDPA: mark frame is NDPA for HT beamformer sequence * @IWM_TX_CMD_FLG_CSI_FDBK2HOST: mark to send feedback to host (only if good CRC) * @IWM_TX_CMD_FLG_BT_DIS: disable BT priority for this frame * @IWM_TX_CMD_FLG_SEQ_CTL: set if FW should override the sequence control. * Should be set for mgmt, non-QOS data, mcast, bcast and in scan command * @IWM_TX_CMD_FLG_MORE_FRAG: this frame is non-last MPDU * @IWM_TX_CMD_FLG_NEXT_FRAME: this frame includes information of the next frame * @IWM_TX_CMD_FLG_TSF: FW should calculate and insert TSF in the frame * Should be set for beacons and probe responses * @IWM_TX_CMD_FLG_CALIB: activate PA TX power calibrations * @IWM_TX_CMD_FLG_KEEP_SEQ_CTL: if seq_ctl is set, don't increase inner seq count * @IWM_TX_CMD_FLG_AGG_START: allow this frame to start aggregation * @IWM_TX_CMD_FLG_MH_PAD: driver inserted 2 byte padding after MAC header. * Should be set for 26/30 length MAC headers * @IWM_TX_CMD_FLG_RESP_TO_DRV: zero this if the response should go only to FW * @IWM_TX_CMD_FLG_CCMP_AGG: this frame uses CCMP for aggregation acceleration * @IWM_TX_CMD_FLG_TKIP_MIC_DONE: FW already performed TKIP MIC calculation * @IWM_TX_CMD_FLG_DUR: disable duration overwriting used in PS-Poll Assoc-id * @IWM_TX_CMD_FLG_FW_DROP: FW should mark frame to be dropped * @IWM_TX_CMD_FLG_EXEC_PAPD: execute PAPD * @IWM_TX_CMD_FLG_PAPD_TYPE: 0 for reference power, 1 for nominal power * @IWM_TX_CMD_FLG_HCCA_CHUNK: mark start of TSPEC chunk */ enum iwm_tx_flags { IWM_TX_CMD_FLG_PROT_REQUIRE = (1 << 0), IWM_TX_CMD_FLG_ACK = (1 << 3), IWM_TX_CMD_FLG_STA_RATE = (1 << 4), IWM_TX_CMD_FLG_BA = (1 << 5), IWM_TX_CMD_FLG_BAR = (1 << 6), IWM_TX_CMD_FLG_TXOP_PROT = (1 << 7), IWM_TX_CMD_FLG_VHT_NDPA = (1 << 8), IWM_TX_CMD_FLG_HT_NDPA = (1 << 9), IWM_TX_CMD_FLG_CSI_FDBK2HOST = (1 << 10), IWM_TX_CMD_FLG_BT_DIS = (1 << 12), IWM_TX_CMD_FLG_SEQ_CTL = (1 << 13), IWM_TX_CMD_FLG_MORE_FRAG = (1 << 14), IWM_TX_CMD_FLG_NEXT_FRAME = (1 << 15), IWM_TX_CMD_FLG_TSF = (1 << 16), IWM_TX_CMD_FLG_CALIB = (1 << 17), IWM_TX_CMD_FLG_KEEP_SEQ_CTL = (1 << 18), IWM_TX_CMD_FLG_AGG_START = (1 << 19), IWM_TX_CMD_FLG_MH_PAD = (1 << 20), IWM_TX_CMD_FLG_RESP_TO_DRV = (1 << 21), IWM_TX_CMD_FLG_CCMP_AGG = (1 << 22), IWM_TX_CMD_FLG_TKIP_MIC_DONE = (1 << 23), IWM_TX_CMD_FLG_DUR = (1 << 25), IWM_TX_CMD_FLG_FW_DROP = (1 << 26), IWM_TX_CMD_FLG_EXEC_PAPD = (1 << 27), IWM_TX_CMD_FLG_PAPD_TYPE = (1 << 28), IWM_TX_CMD_FLG_HCCA_CHUNK = (1 << 31) }; /* IWM_TX_FLAGS_BITS_API_S_VER_1 */ /* * TX command security control */ #define IWM_TX_CMD_SEC_WEP 0x01 #define IWM_TX_CMD_SEC_CCM 0x02 #define IWM_TX_CMD_SEC_TKIP 0x03 #define IWM_TX_CMD_SEC_EXT 0x04 #define IWM_TX_CMD_SEC_MSK 0x07 #define IWM_TX_CMD_SEC_WEP_KEY_IDX_POS 6 #define IWM_TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 #define IWM_TX_CMD_SEC_KEY128 0x08 /* TODO: how does these values are OK with only 16 bit variable??? */ /* * TX command next frame info * * bits 0:2 - security control (IWM_TX_CMD_SEC_*) * bit 3 - immediate ACK required * bit 4 - rate is taken from STA table * bit 5 - frame belongs to BA stream * bit 6 - immediate BA response expected * bit 7 - unused * bits 8:15 - Station ID * bits 16:31 - rate */ #define IWM_TX_CMD_NEXT_FRAME_ACK_MSK (0x8) #define IWM_TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10) #define IWM_TX_CMD_NEXT_FRAME_BA_MSK (0x20) #define IWM_TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40) #define IWM_TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8) #define IWM_TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00) #define IWM_TX_CMD_NEXT_FRAME_STA_ID_POS (8) #define IWM_TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000) #define IWM_TX_CMD_NEXT_FRAME_RATE_POS (16) /* * TX command Frame life time in us - to be written in pm_frame_timeout */ #define IWM_TX_CMD_LIFE_TIME_INFINITE 0xFFFFFFFF #define IWM_TX_CMD_LIFE_TIME_DEFAULT 2000000 /* 2000 ms*/ #define IWM_TX_CMD_LIFE_TIME_PROBE_RESP 40000 /* 40 ms */ #define IWM_TX_CMD_LIFE_TIME_EXPIRED_FRAME 0 /* * TID for non QoS frames - to be written in tid_tspec */ #define IWM_TID_NON_QOS IWM_MAX_TID_COUNT /* * Limits on the retransmissions - to be written in {data,rts}_retry_limit */ #define IWM_DEFAULT_TX_RETRY 15 #define IWM_MGMT_DFAULT_RETRY_LIMIT 3 #define IWM_RTS_DFAULT_RETRY_LIMIT 60 #define IWM_BAR_DFAULT_RETRY_LIMIT 60 #define IWM_LOW_RETRY_LIMIT 7 /* TODO: complete documentation for try_cnt and btkill_cnt */ /** * struct iwm_tx_cmd - TX command struct to FW * ( IWM_TX_CMD = 0x1c ) * @len: in bytes of the payload, see below for details * @next_frame_len: same as len, but for next frame (0 if not applicable) * Used for fragmentation and bursting, but not in 11n aggregation. * @tx_flags: combination of IWM_TX_CMD_FLG_* * @rate_n_flags: rate for *all* Tx attempts, if IWM_TX_CMD_FLG_STA_RATE_MSK is * cleared. Combination of IWM_RATE_MCS_* * @sta_id: index of destination station in FW station table * @sec_ctl: security control, IWM_TX_CMD_SEC_* * @initial_rate_index: index into the rate table for initial TX attempt. * Applied if IWM_TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. * @key: security key * @next_frame_flags: IWM_TX_CMD_SEC_* and IWM_TX_CMD_NEXT_FRAME_* * @life_time: frame life time (usecs??) * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + * btkill_cnd + reserved), first 32 bits. "0" disables usage. * @dram_msb_ptr: upper bits of the scratch physical address * @rts_retry_limit: max attempts for RTS * @data_retry_limit: max attempts to send the data packet * @tid_spec: TID/tspec * @pm_frame_timeout: PM TX frame timeout * @driver_txop: duration od EDCA TXOP, in 32-usec units. Set this if not * specified by HCCA protocol * * The byte count (both len and next_frame_len) includes MAC header * (24/26/30/32 bytes) * + 2 bytes pad if 26/30 header size * + 8 byte IV for CCM or TKIP (not used for WEP) * + Data payload * + 8-byte MIC (not used for CCM/WEP) * It does not include post-MAC padding, i.e., * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes. * Range of len: 14-2342 bytes. * * After the struct fields the MAC header is placed, plus any padding, * and then the actial payload. */ struct iwm_tx_cmd { uint16_t len; uint16_t next_frame_len; uint32_t tx_flags; struct { uint8_t try_cnt; uint8_t btkill_cnt; uint16_t reserved; } scratch; /* DRAM_SCRATCH_API_U_VER_1 */ uint32_t rate_n_flags; uint8_t sta_id; uint8_t sec_ctl; uint8_t initial_rate_index; uint8_t reserved2; uint8_t key[16]; uint16_t next_frame_flags; uint16_t reserved3; uint32_t life_time; uint32_t dram_lsb_ptr; uint8_t dram_msb_ptr; uint8_t rts_retry_limit; uint8_t data_retry_limit; uint8_t tid_tspec; uint16_t pm_frame_timeout; uint16_t driver_txop; uint8_t payload[0]; struct ieee80211_frame hdr[0]; } __packed; /* IWM_TX_CMD_API_S_VER_3 */ /* * TX response related data */ /* * enum iwm_tx_status - status that is returned by the fw after attempts to Tx * @IWM_TX_STATUS_SUCCESS: * @IWM_TX_STATUS_DIRECT_DONE: * @IWM_TX_STATUS_POSTPONE_DELAY: * @IWM_TX_STATUS_POSTPONE_FEW_BYTES: * @IWM_TX_STATUS_POSTPONE_BT_PRIO: * @IWM_TX_STATUS_POSTPONE_QUIET_PERIOD: * @IWM_TX_STATUS_POSTPONE_CALC_TTAK: * @IWM_TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY: * @IWM_TX_STATUS_FAIL_SHORT_LIMIT: * @IWM_TX_STATUS_FAIL_LONG_LIMIT: * @IWM_TX_STATUS_FAIL_UNDERRUN: * @IWM_TX_STATUS_FAIL_DRAIN_FLOW: * @IWM_TX_STATUS_FAIL_RFKILL_FLUSH: * @IWM_TX_STATUS_FAIL_LIFE_EXPIRE: * @IWM_TX_STATUS_FAIL_DEST_PS: * @IWM_TX_STATUS_FAIL_HOST_ABORTED: * @IWM_TX_STATUS_FAIL_BT_RETRY: * @IWM_TX_STATUS_FAIL_STA_INVALID: * @IWM_TX_TATUS_FAIL_FRAG_DROPPED: * @IWM_TX_STATUS_FAIL_TID_DISABLE: * @IWM_TX_STATUS_FAIL_FIFO_FLUSHED: * @IWM_TX_STATUS_FAIL_SMALL_CF_POLL: * @IWM_TX_STATUS_FAIL_FW_DROP: * @IWM_TX_STATUS_FAIL_STA_COLOR_MISMATCH: mismatch between color of Tx cmd and * STA table * @IWM_TX_FRAME_STATUS_INTERNAL_ABORT: * @IWM_TX_MODE_MSK: * @IWM_TX_MODE_NO_BURST: * @IWM_TX_MODE_IN_BURST_SEQ: * @IWM_TX_MODE_FIRST_IN_BURST: * @IWM_TX_QUEUE_NUM_MSK: * * Valid only if frame_count =1 * TODO: complete documentation */ enum iwm_tx_status { IWM_TX_STATUS_MSK = 0x000000ff, IWM_TX_STATUS_SUCCESS = 0x01, IWM_TX_STATUS_DIRECT_DONE = 0x02, /* postpone TX */ IWM_TX_STATUS_POSTPONE_DELAY = 0x40, IWM_TX_STATUS_POSTPONE_FEW_BYTES = 0x41, IWM_TX_STATUS_POSTPONE_BT_PRIO = 0x42, IWM_TX_STATUS_POSTPONE_QUIET_PERIOD = 0x43, IWM_TX_STATUS_POSTPONE_CALC_TTAK = 0x44, /* abort TX */ IWM_TX_STATUS_FAIL_INTERNAL_CROSSED_RETRY = 0x81, IWM_TX_STATUS_FAIL_SHORT_LIMIT = 0x82, IWM_TX_STATUS_FAIL_LONG_LIMIT = 0x83, IWM_TX_STATUS_FAIL_UNDERRUN = 0x84, IWM_TX_STATUS_FAIL_DRAIN_FLOW = 0x85, IWM_TX_STATUS_FAIL_RFKILL_FLUSH = 0x86, IWM_TX_STATUS_FAIL_LIFE_EXPIRE = 0x87, IWM_TX_STATUS_FAIL_DEST_PS = 0x88, IWM_TX_STATUS_FAIL_HOST_ABORTED = 0x89, IWM_TX_STATUS_FAIL_BT_RETRY = 0x8a, IWM_TX_STATUS_FAIL_STA_INVALID = 0x8b, IWM_TX_STATUS_FAIL_FRAG_DROPPED = 0x8c, IWM_TX_STATUS_FAIL_TID_DISABLE = 0x8d, IWM_TX_STATUS_FAIL_FIFO_FLUSHED = 0x8e, IWM_TX_STATUS_FAIL_SMALL_CF_POLL = 0x8f, IWM_TX_STATUS_FAIL_FW_DROP = 0x90, IWM_TX_STATUS_FAIL_STA_COLOR_MISMATCH = 0x91, IWM_TX_STATUS_INTERNAL_ABORT = 0x92, IWM_TX_MODE_MSK = 0x00000f00, IWM_TX_MODE_NO_BURST = 0x00000000, IWM_TX_MODE_IN_BURST_SEQ = 0x00000100, IWM_TX_MODE_FIRST_IN_BURST = 0x00000200, IWM_TX_QUEUE_NUM_MSK = 0x0001f000, IWM_TX_NARROW_BW_MSK = 0x00060000, IWM_TX_NARROW_BW_1DIV2 = 0x00020000, IWM_TX_NARROW_BW_1DIV4 = 0x00040000, IWM_TX_NARROW_BW_1DIV8 = 0x00060000, }; /* * enum iwm_tx_agg_status - TX aggregation status * @IWM_AGG_TX_STATE_STATUS_MSK: * @IWM_AGG_TX_STATE_TRANSMITTED: * @IWM_AGG_TX_STATE_UNDERRUN: * @IWM_AGG_TX_STATE_BT_PRIO: * @IWM_AGG_TX_STATE_FEW_BYTES: * @IWM_AGG_TX_STATE_ABORT: * @IWM_AGG_TX_STATE_LAST_SENT_TTL: * @IWM_AGG_TX_STATE_LAST_SENT_TRY_CNT: * @IWM_AGG_TX_STATE_LAST_SENT_BT_KILL: * @IWM_AGG_TX_STATE_SCD_QUERY: * @IWM_AGG_TX_STATE_TEST_BAD_CRC32: * @IWM_AGG_TX_STATE_RESPONSE: * @IWM_AGG_TX_STATE_DUMP_TX: * @IWM_AGG_TX_STATE_DELAY_TX: * @IWM_AGG_TX_STATE_TRY_CNT_MSK: Retry count for 1st frame in aggregation (retries * occur if tx failed for this frame when it was a member of a previous * aggregation block). If rate scaling is used, retry count indicates the * rate table entry used for all frames in the new agg. *@ IWM_AGG_TX_STATE_SEQ_NUM_MSK: Command ID and sequence number of Tx command for * this frame * * TODO: complete documentation */ enum iwm_tx_agg_status { IWM_AGG_TX_STATE_STATUS_MSK = 0x00fff, IWM_AGG_TX_STATE_TRANSMITTED = 0x000, IWM_AGG_TX_STATE_UNDERRUN = 0x001, IWM_AGG_TX_STATE_BT_PRIO = 0x002, IWM_AGG_TX_STATE_FEW_BYTES = 0x004, IWM_AGG_TX_STATE_ABORT = 0x008, IWM_AGG_TX_STATE_LAST_SENT_TTL = 0x010, IWM_AGG_TX_STATE_LAST_SENT_TRY_CNT = 0x020, IWM_AGG_TX_STATE_LAST_SENT_BT_KILL = 0x040, IWM_AGG_TX_STATE_SCD_QUERY = 0x080, IWM_AGG_TX_STATE_TEST_BAD_CRC32 = 0x0100, IWM_AGG_TX_STATE_RESPONSE = 0x1ff, IWM_AGG_TX_STATE_DUMP_TX = 0x200, IWM_AGG_TX_STATE_DELAY_TX = 0x400, IWM_AGG_TX_STATE_TRY_CNT_POS = 12, IWM_AGG_TX_STATE_TRY_CNT_MSK = 0xf << IWM_AGG_TX_STATE_TRY_CNT_POS, }; #define IWM_AGG_TX_STATE_LAST_SENT_MSK (IWM_AGG_TX_STATE_LAST_SENT_TTL| \ IWM_AGG_TX_STATE_LAST_SENT_TRY_CNT| \ IWM_AGG_TX_STATE_LAST_SENT_BT_KILL) /* * The mask below describes a status where we are absolutely sure that the MPDU * wasn't sent. For BA/Underrun we cannot be that sure. All we know that we've * written the bytes to the TXE, but we know nothing about what the DSP did. */ #define IWM_AGG_TX_STAT_FRAME_NOT_SENT (IWM_AGG_TX_STATE_FEW_BYTES | \ IWM_AGG_TX_STATE_ABORT | \ IWM_AGG_TX_STATE_SCD_QUERY) /* * IWM_REPLY_TX = 0x1c (response) * * This response may be in one of two slightly different formats, indicated * by the frame_count field: * * 1) No aggregation (frame_count == 1). This reports Tx results for a single * frame. Multiple attempts, at various bit rates, may have been made for * this frame. * * 2) Aggregation (frame_count > 1). This reports Tx results for two or more * frames that used block-acknowledge. All frames were transmitted at * same rate. Rate scaling may have been used if first frame in this new * agg block failed in previous agg block(s). * * Note that, for aggregation, ACK (block-ack) status is not delivered * here; block-ack has not been received by the time the device records * this status. * This status relates to reasons the tx might have been blocked or aborted * within the device, rather than whether it was received successfully by * the destination station. */ /** * struct iwm_agg_tx_status - per packet TX aggregation status * @status: enum iwm_tx_agg_status * @sequence: Sequence # for this frame's Tx cmd (not SSN!) */ struct iwm_agg_tx_status { uint16_t status; uint16_t sequence; } __packed; /* * definitions for initial rate index field * bits [3:0] initial rate index * bits [6:4] rate table color, used for the initial rate * bit-7 invalid rate indication */ #define IWM_TX_RES_INIT_RATE_INDEX_MSK 0x0f #define IWM_TX_RES_RATE_TABLE_COLOR_MSK 0x70 #define IWM_TX_RES_INV_RATE_INDEX_MSK 0x80 #define IWM_MVM_TX_RES_GET_TID(_ra_tid) ((_ra_tid) & 0x0f) #define IWM_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) /** * struct iwm_mvm_tx_resp - notifies that fw is TXing a packet * ( IWM_REPLY_TX = 0x1c ) * @frame_count: 1 no aggregation, >1 aggregation * @bt_kill_count: num of times blocked by bluetooth (unused for agg) * @failure_rts: num of failures due to unsuccessful RTS * @failure_frame: num failures due to no ACK (unused for agg) * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the * Tx of all the batch. IWM_RATE_MCS_* * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. * for agg: RTS + CTS + aggregation tx time + block-ack time. * in usec. * @pa_status: tx power info * @pa_integ_res_a: tx power info * @pa_integ_res_b: tx power info * @pa_integ_res_c: tx power info * @measurement_req_id: tx power info * @tfd_info: TFD information set by the FH * @seq_ctl: sequence control from the Tx cmd * @byte_cnt: byte count from the Tx cmd * @tlc_info: TLC rate info * @ra_tid: bits [3:0] = ra, bits [7:4] = tid * @frame_ctrl: frame control * @status: for non-agg: frame status IWM_TX_STATUS_* * for agg: status of 1st frame, IWM_AGG_TX_STATE_*; other frame status fields * follow this one, up to frame_count. * * After the array of statuses comes the SSN of the SCD. Look at * %iwm_mvm_get_scd_ssn for more details. */ struct iwm_mvm_tx_resp { uint8_t frame_count; uint8_t bt_kill_count; uint8_t failure_rts; uint8_t failure_frame; uint32_t initial_rate; uint16_t wireless_media_time; uint8_t pa_status; uint8_t pa_integ_res_a[3]; uint8_t pa_integ_res_b[3]; uint8_t pa_integ_res_c[3]; uint16_t measurement_req_id; uint16_t reserved; uint32_t tfd_info; uint16_t seq_ctl; uint16_t byte_cnt; uint8_t tlc_info; uint8_t ra_tid; uint16_t frame_ctrl; struct iwm_agg_tx_status status; } __packed; /* IWM_TX_RSP_API_S_VER_3 */ /** * struct iwm_mvm_ba_notif - notifies about reception of BA * ( IWM_BA_NOTIF = 0xc5 ) * @sta_addr_lo32: lower 32 bits of the MAC address * @sta_addr_hi16: upper 16 bits of the MAC address * @sta_id: Index of recipient (BA-sending) station in fw's station table * @tid: tid of the session * @seq_ctl: * @bitmap: the bitmap of the BA notification as seen in the air * @scd_flow: the tx queue this BA relates to * @scd_ssn: the index of the last contiguously sent packet * @txed: number of Txed frames in this batch * @txed_2_done: number of Acked frames in this batch */ struct iwm_mvm_ba_notif { uint32_t sta_addr_lo32; uint16_t sta_addr_hi16; uint16_t reserved; uint8_t sta_id; uint8_t tid; uint16_t seq_ctl; uint64_t bitmap; uint16_t scd_flow; uint16_t scd_ssn; uint8_t txed; uint8_t txed_2_done; uint16_t reserved1; } __packed; /* * struct iwm_mac_beacon_cmd - beacon template command * @tx: the tx commands associated with the beacon frame * @template_id: currently equal to the mac context id of the coresponding * mac. * @tim_idx: the offset of the tim IE in the beacon * @tim_size: the length of the tim IE * @frame: the template of the beacon frame */ struct iwm_mac_beacon_cmd { struct iwm_tx_cmd tx; uint32_t template_id; uint32_t tim_idx; uint32_t tim_size; struct ieee80211_frame frame[0]; } __packed; struct iwm_beacon_notif { struct iwm_mvm_tx_resp beacon_notify_hdr; uint64_t tsf; uint32_t ibss_mgr_status; } __packed; /** * enum iwm_dump_control - dump (flush) control flags * @IWM_DUMP_TX_FIFO_FLUSH: Dump MSDUs until the FIFO is empty * and the TFD queues are empty. */ enum iwm_dump_control { IWM_DUMP_TX_FIFO_FLUSH = (1 << 1), }; /** * struct iwm_tx_path_flush_cmd -- queue/FIFO flush command * @queues_ctl: bitmap of queues to flush * @flush_ctl: control flags * @reserved: reserved */ struct iwm_tx_path_flush_cmd { uint32_t queues_ctl; uint16_t flush_ctl; uint16_t reserved; } __packed; /* IWM_TX_PATH_FLUSH_CMD_API_S_VER_1 */ /** * iwm_mvm_get_scd_ssn - returns the SSN of the SCD * @tx_resp: the Tx response from the fw (agg or non-agg) * * When the fw sends an AMPDU, it fetches the MPDUs one after the other. Since * it can't know that everything will go well until the end of the AMPDU, it * can't know in advance the number of MPDUs that will be sent in the current * batch. This is why it writes the agg Tx response while it fetches the MPDUs. * Hence, it can't know in advance what the SSN of the SCD will be at the end * of the batch. This is why the SSN of the SCD is written at the end of the * whole struct at a variable offset. This function knows how to cope with the * variable offset and returns the SSN of the SCD. */ static inline uint32_t iwm_mvm_get_scd_ssn(struct iwm_mvm_tx_resp *tx_resp) { return le32_to_cpup((uint32_t *)&tx_resp->status + tx_resp->frame_count) & 0xfff; } /* * END mvm/fw-api-tx.h */ /* * BEGIN mvm/fw-api-scan.h */ +/** + * struct iwm_scd_txq_cfg_cmd - New txq hw scheduler config command + * @token: + * @sta_id: station id + * @tid: + * @scd_queue: scheduler queue to confiug + * @enable: 1 queue enable, 0 queue disable + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: %enum iwm_mvm_tx_fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + */ +struct iwm_scd_txq_cfg_cmd { + uint8_t token; + uint8_t sta_id; + uint8_t tid; + uint8_t scd_queue; + uint8_t enable; + uint8_t aggregate; + uint8_t tx_fifo; + uint8_t window; + uint16_t ssn; + uint16_t reserved; +} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwm_scd_txq_cfg_rsp + * @token: taken from the command + * @sta_id: station id from the command + * @tid: tid from the command + * @scd_queue: scd_queue from the command + */ +struct iwm_scd_txq_cfg_rsp { + uint8_t token; + uint8_t sta_id; + uint8_t tid; + uint8_t scd_queue; +} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ + + /* Scan Commands, Responses, Notifications */ /* Masks for iwm_scan_channel.type flags */ #define IWM_SCAN_CHANNEL_TYPE_ACTIVE (1 << 0) #define IWM_SCAN_CHANNEL_NARROW_BAND (1 << 22) /* Max number of IEs for direct SSID scans in a command */ #define IWM_PROBE_OPTION_MAX 20 /** * struct iwm_scan_channel - entry in IWM_REPLY_SCAN_CMD channel table * @channel: band is selected by iwm_scan_cmd "flags" field * @tx_gain: gain for analog radio * @dsp_atten: gain for DSP * @active_dwell: dwell time for active scan in TU, typically 5-50 * @passive_dwell: dwell time for passive scan in TU, typically 20-500 * @type: type is broken down to these bits: * bit 0: 0 = passive, 1 = active * bits 1-20: SSID direct bit map. If any of these bits is set then * the corresponding SSID IE is transmitted in probe request * (bit i adds IE in position i to the probe request) * bit 22: channel width, 0 = regular, 1 = TGj narrow channel * * @iteration_count: * @iteration_interval: * This struct is used once for each channel in the scan list. * Each channel can independently select: * 1) SSID for directed active scans * 2) Txpower setting (for rate specified within Tx command) * 3) How long to stay on-channel (behavior may be modified by quiet_time, * quiet_plcp_th, good_CRC_th) * * To avoid uCode errors, make sure the following are true (see comments * under struct iwm_scan_cmd about max_out_time and quiet_time): * 1) If using passive_dwell (i.e. passive_dwell != 0): * active_dwell <= passive_dwell (< max_out_time if max_out_time != 0) * 2) quiet_time <= active_dwell * 3) If restricting off-channel time (i.e. max_out_time !=0): * passive_dwell < max_out_time * active_dwell < max_out_time */ struct iwm_scan_channel { uint32_t type; uint16_t channel; uint16_t iteration_count; uint32_t iteration_interval; uint16_t active_dwell; uint16_t passive_dwell; } __packed; /* IWM_SCAN_CHANNEL_CONTROL_API_S_VER_1 */ /** * struct iwm_ssid_ie - directed scan network information element * * Up to 20 of these may appear in IWM_REPLY_SCAN_CMD, * selected by "type" bit field in struct iwm_scan_channel; * each channel may select different ssids from among the 20 entries. * SSID IEs get transmitted in reverse order of entry. */ struct iwm_ssid_ie { uint8_t id; uint8_t len; uint8_t ssid[IEEE80211_NWID_LEN]; } __packed; /* IWM_SCAN_DIRECT_SSID_IE_API_S_VER_1 */ +/* scan offload */ +#define IWM_MAX_SCAN_CHANNELS 40 +#define IWM_SCAN_MAX_BLACKLIST_LEN 64 +#define IWM_SCAN_SHORT_BLACKLIST_LEN 16 +#define IWM_SCAN_MAX_PROFILES 11 +#define IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE 512 + +/* Default watchdog (in MS) for scheduled scan iteration */ +#define IWM_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) + +#define IWM_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWM_CAN_ABORT_STATUS 1 + +#define IWM_FULL_SCAN_MULTIPLIER 5 +#define IWM_FAST_SCHED_SCAN_ITERATIONS 3 +#define IWM_MAX_SCHED_SCAN_PLANS 2 + /** * iwm_scan_flags - masks for scan command flags *@IWM_SCAN_FLAGS_PERIODIC_SCAN: *@IWM_SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX: *@IWM_SCAN_FLAGS_DELAYED_SCAN_LOWBAND: *@IWM_SCAN_FLAGS_DELAYED_SCAN_HIGHBAND: *@IWM_SCAN_FLAGS_FRAGMENTED_SCAN: *@IWM_SCAN_FLAGS_PASSIVE2ACTIVE: use active scan on channels that was active * in the past hour, even if they are marked as passive. */ enum iwm_scan_flags { IWM_SCAN_FLAGS_PERIODIC_SCAN = (1 << 0), IWM_SCAN_FLAGS_P2P_PUBLIC_ACTION_FRAME_TX = (1 << 1), IWM_SCAN_FLAGS_DELAYED_SCAN_LOWBAND = (1 << 2), IWM_SCAN_FLAGS_DELAYED_SCAN_HIGHBAND = (1 << 3), IWM_SCAN_FLAGS_FRAGMENTED_SCAN = (1 << 4), IWM_SCAN_FLAGS_PASSIVE2ACTIVE = (1 << 5), }; /** * enum iwm_scan_type - Scan types for scan command * @IWM_SCAN_TYPE_FORCED: * @IWM_SCAN_TYPE_BACKGROUND: * @IWM_SCAN_TYPE_OS: * @IWM_SCAN_TYPE_ROAMING: * @IWM_SCAN_TYPE_ACTION: * @IWM_SCAN_TYPE_DISCOVERY: * @IWM_SCAN_TYPE_DISCOVERY_FORCED: */ enum iwm_scan_type { IWM_SCAN_TYPE_FORCED = 0, IWM_SCAN_TYPE_BACKGROUND = 1, IWM_SCAN_TYPE_OS = 2, IWM_SCAN_TYPE_ROAMING = 3, IWM_SCAN_TYPE_ACTION = 4, IWM_SCAN_TYPE_DISCOVERY = 5, IWM_SCAN_TYPE_DISCOVERY_FORCED = 6, }; /* IWM_SCAN_ACTIVITY_TYPE_E_VER_1 */ /* Maximal number of channels to scan */ #define IWM_MAX_NUM_SCAN_CHANNELS 0x24 /** - * struct iwm_scan_cmd - scan request command - * ( IWM_SCAN_REQUEST_CMD = 0x80 ) - * @len: command length in bytes - * @scan_flags: scan flags from IWM_SCAN_FLAGS_* - * @channel_count: num of channels in channel list (1 - IWM_MAX_NUM_SCAN_CHANNELS) - * @quiet_time: in msecs, dwell this time for active scan on quiet channels - * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than - * this number of packets were received (typically 1) - * @passive2active: is auto switching from passive to active during scan allowed - * @rxchain_sel_flags: RXON_RX_CHAIN_* - * @max_out_time: in usecs, max out of serving channel time - * @suspend_time: how long to pause scan when returning to service channel: - * bits 0-19: beacon interval in usecs (suspend before executing) - * bits 20-23: reserved - * bits 24-31: number of beacons (suspend between channels) - * @rxon_flags: RXON_FLG_* - * @filter_flags: RXON_FILTER_* - * @tx_cmd: for active scans (zero for passive), w/o payload, - * no RS so specify TX rate - * @direct_scan: direct scan SSIDs - * @type: one of IWM_SCAN_TYPE_* - * @repeats: how many time to repeat the scan + * iwm_scan_schedule_lmac - schedule of scan offload + * @delay: delay between iterations, in seconds. + * @iterations: num of scan iterations + * @full_scan_mul: number of partial scans before each full scan */ -struct iwm_scan_cmd { +struct iwm_scan_schedule_lmac { + uint16_t delay; + uint8_t iterations; + uint8_t full_scan_mul; +} __packed; /* SCAN_SCHEDULE_API_S */ + +/** + * iwm_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @reserved: for alignment and future use + */ +struct iwm_scan_req_tx_cmd { + uint32_t tx_flags; + uint32_t rate_n_flags; + uint8_t sta_id; + uint8_t reserved[3]; +} __packed; + +enum iwm_scan_channel_flags_lmac { + IWM_UNIFIED_SCAN_CHANNEL_FULL = (1 << 27), + IWM_UNIFIED_SCAN_CHANNEL_PARTIAL = (1 << 28), +}; + +/** + * iwm_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 + * @flags: bits 1-20: directed scan to i'th ssid + * other bits &enum iwm_scan_channel_flags_lmac + * @channel_number: channel number 1-13 etc + * @iter_count: scan iteration on this channel + * @iter_interval: interval in seconds between iterations on one channel + */ +struct iwm_scan_channel_cfg_lmac { + uint32_t flags; + uint16_t channel_num; + uint16_t iter_count; + uint32_t iter_interval; +} __packed; + +/* + * iwm_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 + * @offset: offset in the data block + * @len: length of the segment + */ +struct iwm_scan_probe_segment { + uint16_t offset; uint16_t len; - uint8_t scan_flags; - uint8_t channel_count; - uint16_t quiet_time; - uint16_t quiet_plcp_th; - uint16_t passive2active; - uint16_t rxchain_sel_flags; +} __packed; + +/* iwm_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 + * @mac_header: first (and common) part of the probe + * @band_data: band specific data + * @common_data: last (and common) part of the probe + * @buf: raw data block + */ +struct iwm_scan_probe_req { + struct iwm_scan_probe_segment mac_header; + struct iwm_scan_probe_segment band_data[2]; + struct iwm_scan_probe_segment common_data; + uint8_t buf[IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE]; +} __packed; + +enum iwm_scan_channel_flags { + IWM_SCAN_CHANNEL_FLAG_EBS = (1 << 0), + IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE = (1 << 1), + IWM_SCAN_CHANNEL_FLAG_CACHE_ADD = (1 << 2), +}; + +/* iwm_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S + * @flags: enum iwm_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). + */ +struct iwm_scan_channel_opt { + uint16_t flags; + uint16_t non_ebs_ratio; +} __packed; + +/** + * iwm_mvm_lmac_scan_flags + * @IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses + * without filtering. + * @IWM_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels + * @IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan + * @IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification + * @IWM_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching + * @IWM_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented + * @IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report + * and DS parameter set IEs into probe requests. + * @IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels + * 1, 6 and 11. + * @IWM_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches + */ +enum iwm_mvm_lmac_scan_flags { + IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL = (1 << 0), + IWM_MVM_LMAC_SCAN_FLAG_PASSIVE = (1 << 1), + IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = (1 << 2), + IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = (1 << 3), + IWM_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = (1 << 4), + IWM_MVM_LMAC_SCAN_FLAG_FRAGMENTED = (1 << 5), + IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = (1 << 6), + IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = (1 << 7), + IWM_MVM_LMAC_SCAN_FLAG_MATCH = (1 << 9), +}; + +enum iwm_scan_priority { + IWM_SCAN_PRIORITY_LOW, + IWM_SCAN_PRIORITY_MEDIUM, + IWM_SCAN_PRIORITY_HIGH, +}; + +/** + * iwm_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 + * @reserved1: for alignment and future use + * @channel_num: num of channels to scan + * @active-dwell: dwell time for active channels + * @passive-dwell: dwell time for passive channels + * @fragmented-dwell: dwell time for fragmented passive scan + * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases) + * @reserved2: for alignment and future use + * @rx_chain_selct: PHY_RX_CHAIN_* flags + * @scan_flags: &enum iwm_mvm_lmac_scan_flags + * @max_out_time: max time (in TU) to be out of associated channel + * @suspend_time: pause scan this long (TUs) when returning to service channel + * @flags: RXON flags + * @filter_flags: RXON filter + * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz + * @direct_scan: list of SSIDs for directed active scan + * @scan_prio: enum iwm_scan_priority + * @iter_num: number of scan iterations + * @delay: delay in seconds before first iteration + * @schedule: two scheduling plans. The first one is finite, the second one can + * be infinite. + * @channel_opt: channel optimization options, for full and partial scan + * @data: channel configuration and probe request packet. + */ +struct iwm_scan_req_lmac { + /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ + uint32_t reserved1; + uint8_t n_channels; + uint8_t active_dwell; + uint8_t passive_dwell; + uint8_t fragmented_dwell; + uint8_t extended_dwell; + uint8_t reserved2; + uint16_t rx_chain_select; + uint32_t scan_flags; uint32_t max_out_time; uint32_t suspend_time; - /* IWM_RX_ON_FLAGS_API_S_VER_1 */ - uint32_t rxon_flags; + /* RX_ON_FLAGS_API_S_VER_1 */ + uint32_t flags; uint32_t filter_flags; - struct iwm_tx_cmd tx_cmd; + struct iwm_scan_req_tx_cmd tx_cmd[2]; struct iwm_ssid_ie direct_scan[IWM_PROBE_OPTION_MAX]; - uint32_t type; - uint32_t repeats; + uint32_t scan_prio; + /* SCAN_REQ_PERIODIC_PARAMS_API_S */ + uint32_t iter_num; + uint32_t delay; + struct iwm_scan_schedule_lmac schedule[IWM_MAX_SCHED_SCAN_PLANS]; + struct iwm_scan_channel_opt channel_opt[2]; + uint8_t data[]; +} __packed; - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct iwm_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive IWM_SCAN_COMPLETE_NOTIFICATION) - * before requesting another scan. - */ - uint8_t data[0]; -} __packed; /* IWM_SCAN_REQUEST_FIXED_PART_API_S_VER_5 */ +/** + * iwm_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 + * @last_schedule_line: last schedule line executed (fast or regular) + * @last_schedule_iteration: last scan iteration executed before scan abort + * @status: enum iwm_scan_offload_complete_status + * @ebs_status: EBS success status &enum iwm_scan_ebs_status + * @time_after_last_iter; time in seconds elapsed after last iteration + */ +struct iwm_periodic_scan_complete { + uint8_t last_schedule_line; + uint8_t last_schedule_iteration; + uint8_t status; + uint8_t ebs_status; + uint32_t time_after_last_iter; + uint32_t reserved; +} __packed; /* Response to scan request contains only status with one of these values */ #define IWM_SCAN_RESPONSE_OK 0x1 #define IWM_SCAN_RESPONSE_ERROR 0x2 /* * IWM_SCAN_ABORT_CMD = 0x81 * When scan abort is requested, the command has no fields except the common * header. The response contains only a status with one of these values. */ #define IWM_SCAN_ABORT_POSSIBLE 0x1 #define IWM_SCAN_ABORT_IGNORED 0x2 /* no pending scans */ /* TODO: complete documentation */ #define IWM_SCAN_OWNER_STATUS 0x1 #define IWM_MEASURE_OWNER_STATUS 0x2 /** * struct iwm_scan_start_notif - notifies start of scan in the device * ( IWM_SCAN_START_NOTIFICATION = 0x82 ) * @tsf_low: TSF timer (lower half) in usecs * @tsf_high: TSF timer (higher half) in usecs * @beacon_timer: structured as follows: * bits 0:19 - beacon interval in usecs * bits 20:23 - reserved (0) * bits 24:31 - number of beacons * @channel: which channel is scanned * @band: 0 for 5.2 GHz, 1 for 2.4 GHz * @status: one of *_OWNER_STATUS */ struct iwm_scan_start_notif { uint32_t tsf_low; uint32_t tsf_high; uint32_t beacon_timer; uint8_t channel; uint8_t band; uint8_t reserved[2]; uint32_t status; } __packed; /* IWM_SCAN_START_NTF_API_S_VER_1 */ /* scan results probe_status first bit indicates success */ #define IWM_SCAN_PROBE_STATUS_OK 0 #define IWM_SCAN_PROBE_STATUS_TX_FAILED (1 << 0) /* error statuses combined with TX_FAILED */ #define IWM_SCAN_PROBE_STATUS_FAIL_TTL (1 << 1) #define IWM_SCAN_PROBE_STATUS_FAIL_BT (1 << 2) /* How many statistics are gathered for each channel */ #define IWM_SCAN_RESULTS_STATISTICS 1 /** * enum iwm_scan_complete_status - status codes for scan complete notifications * @IWM_SCAN_COMP_STATUS_OK: scan completed successfully * @IWM_SCAN_COMP_STATUS_ABORT: scan was aborted by user * @IWM_SCAN_COMP_STATUS_ERR_SLEEP: sending null sleep packet failed * @IWM_SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT: timeout before channel is ready * @IWM_SCAN_COMP_STATUS_ERR_PROBE: sending probe request failed * @IWM_SCAN_COMP_STATUS_ERR_WAKEUP: sending null wakeup packet failed * @IWM_SCAN_COMP_STATUS_ERR_ANTENNAS: invalid antennas chosen at scan command * @IWM_SCAN_COMP_STATUS_ERR_INTERNAL: internal error caused scan abort * @IWM_SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax * @IWM_SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful * (not an error!) * @IWM_SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver * asked for * @IWM_SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events */ enum iwm_scan_complete_status { IWM_SCAN_COMP_STATUS_OK = 0x1, IWM_SCAN_COMP_STATUS_ABORT = 0x2, IWM_SCAN_COMP_STATUS_ERR_SLEEP = 0x3, IWM_SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT = 0x4, IWM_SCAN_COMP_STATUS_ERR_PROBE = 0x5, IWM_SCAN_COMP_STATUS_ERR_WAKEUP = 0x6, IWM_SCAN_COMP_STATUS_ERR_ANTENNAS = 0x7, IWM_SCAN_COMP_STATUS_ERR_INTERNAL = 0x8, IWM_SCAN_COMP_STATUS_ERR_COEX = 0x9, IWM_SCAN_COMP_STATUS_P2P_ACTION_OK = 0xA, IWM_SCAN_COMP_STATUS_ITERATION_END = 0x0B, IWM_SCAN_COMP_STATUS_ERR_ALLOC_TE = 0x0C, }; /** * struct iwm_scan_results_notif - scan results for one channel * ( IWM_SCAN_RESULTS_NOTIFICATION = 0x83 ) * @channel: which channel the results are from * @band: 0 for 5.2 GHz, 1 for 2.4 GHz * @probe_status: IWM_SCAN_PROBE_STATUS_*, indicates success of probe request * @num_probe_not_sent: # of request that weren't sent due to not enough time * @duration: duration spent in channel, in usecs * @statistics: statistics gathered for this channel */ struct iwm_scan_results_notif { uint8_t channel; uint8_t band; uint8_t probe_status; uint8_t num_probe_not_sent; uint32_t duration; uint32_t statistics[IWM_SCAN_RESULTS_STATISTICS]; } __packed; /* IWM_SCAN_RESULT_NTF_API_S_VER_2 */ /** * struct iwm_scan_complete_notif - notifies end of scanning (all channels) * ( IWM_SCAN_COMPLETE_NOTIFICATION = 0x84 ) * @scanned_channels: number of channels scanned (and number of valid results) * @status: one of IWM_SCAN_COMP_STATUS_* * @bt_status: BT on/off status * @last_channel: last channel that was scanned * @tsf_low: TSF timer (lower half) in usecs * @tsf_high: TSF timer (higher half) in usecs * @results: all scan results, only "scanned_channels" of them are valid */ struct iwm_scan_complete_notif { uint8_t scanned_channels; uint8_t status; uint8_t bt_status; uint8_t last_channel; uint32_t tsf_low; uint32_t tsf_high; struct iwm_scan_results_notif results[IWM_MAX_NUM_SCAN_CHANNELS]; } __packed; /* IWM_SCAN_COMPLETE_NTF_API_S_VER_2 */ -/* scan offload */ -#define IWM_MAX_SCAN_CHANNELS 40 -#define IWM_SCAN_MAX_BLACKLIST_LEN 64 -#define IWM_SCAN_SHORT_BLACKLIST_LEN 16 -#define IWM_SCAN_MAX_PROFILES 11 -#define IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE 512 - -/* Default watchdog (in MS) for scheduled scan iteration */ -#define IWM_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) - -#define IWM_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define IWM_CAN_ABORT_STATUS 1 - -#define IWM_FULL_SCAN_MULTIPLIER 5 -#define IWM_FAST_SCHED_SCAN_ITERATIONS 3 - enum iwm_scan_framework_client { IWM_SCAN_CLIENT_SCHED_SCAN = (1 << 0), IWM_SCAN_CLIENT_NETDETECT = (1 << 1), IWM_SCAN_CLIENT_ASSET_TRACKING = (1 << 2), }; /** * struct iwm_scan_offload_cmd - IWM_SCAN_REQUEST_FIXED_PART_API_S_VER_6 * @scan_flags: see enum iwm_scan_flags * @channel_count: channels in channel list * @quiet_time: dwell time, in milisiconds, on quiet channel * @quiet_plcp_th: quiet channel num of packets threshold * @good_CRC_th: passive to active promotion threshold * @rx_chain: RXON rx chain. * @max_out_time: max uSec to be out of assoceated channel * @suspend_time: pause scan this long when returning to service channel * @flags: RXON flags * @filter_flags: RXONfilter * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz. * @direct_scan: list of SSIDs for directed active scan * @scan_type: see enum iwm_scan_type. * @rep_count: repetition count for each scheduled scan iteration. */ struct iwm_scan_offload_cmd { uint16_t len; uint8_t scan_flags; uint8_t channel_count; uint16_t quiet_time; uint16_t quiet_plcp_th; uint16_t good_CRC_th; uint16_t rx_chain; uint32_t max_out_time; uint32_t suspend_time; /* IWM_RX_ON_FLAGS_API_S_VER_1 */ uint32_t flags; uint32_t filter_flags; struct iwm_tx_cmd tx_cmd[2]; /* IWM_SCAN_DIRECT_SSID_IE_API_S_VER_1 */ struct iwm_ssid_ie direct_scan[IWM_PROBE_OPTION_MAX]; uint32_t scan_type; uint32_t rep_count; } __packed; enum iwm_scan_offload_channel_flags { IWM_SCAN_OFFLOAD_CHANNEL_ACTIVE = (1 << 0), IWM_SCAN_OFFLOAD_CHANNEL_NARROW = (1 << 22), IWM_SCAN_OFFLOAD_CHANNEL_FULL = (1 << 24), IWM_SCAN_OFFLOAD_CHANNEL_PARTIAL = (1 << 25), }; /** * iwm_scan_channel_cfg - IWM_SCAN_CHANNEL_CFG_S * @type: bitmap - see enum iwm_scan_offload_channel_flags. * 0: passive (0) or active (1) scan. * 1-20: directed scan to i'th ssid. * 22: channel width configuation - 1 for narrow. * 24: full scan. * 25: partial scan. * @channel_number: channel number 1-13 etc. * @iter_count: repetition count for the channel. * @iter_interval: interval between two innteration on one channel. * @dwell_time: entry 0 - active scan, entry 1 - passive scan. */ struct iwm_scan_channel_cfg { uint32_t type[IWM_MAX_SCAN_CHANNELS]; uint16_t channel_number[IWM_MAX_SCAN_CHANNELS]; uint16_t iter_count[IWM_MAX_SCAN_CHANNELS]; uint32_t iter_interval[IWM_MAX_SCAN_CHANNELS]; uint8_t dwell_time[IWM_MAX_SCAN_CHANNELS][2]; } __packed; /** * iwm_scan_offload_cfg - IWM_SCAN_OFFLOAD_CONFIG_API_S * @scan_cmd: scan command fixed part * @channel_cfg: scan channel configuration * @data: probe request frames (one per band) */ struct iwm_scan_offload_cfg { struct iwm_scan_offload_cmd scan_cmd; struct iwm_scan_channel_cfg channel_cfg; uint8_t data[0]; } __packed; /** * iwm_scan_offload_blacklist - IWM_SCAN_OFFLOAD_BLACKLIST_S * @ssid: MAC address to filter out * @reported_rssi: AP rssi reported to the host * @client_bitmap: clients ignore this entry - enum scan_framework_client */ struct iwm_scan_offload_blacklist { uint8_t ssid[IEEE80211_ADDR_LEN]; uint8_t reported_rssi; uint8_t client_bitmap; } __packed; enum iwm_scan_offload_network_type { IWM_NETWORK_TYPE_BSS = 1, IWM_NETWORK_TYPE_IBSS = 2, IWM_NETWORK_TYPE_ANY = 3, }; enum iwm_scan_offload_band_selection { IWM_SCAN_OFFLOAD_SELECT_2_4 = 0x4, IWM_SCAN_OFFLOAD_SELECT_5_2 = 0x8, IWM_SCAN_OFFLOAD_SELECT_ANY = 0xc, }; /** * iwm_scan_offload_profile - IWM_SCAN_OFFLOAD_PROFILE_S * @ssid_index: index to ssid list in fixed part * @unicast_cipher: encryption olgorithm to match - bitmap * @aut_alg: authentication olgorithm to match - bitmap * @network_type: enum iwm_scan_offload_network_type * @band_selection: enum iwm_scan_offload_band_selection * @client_bitmap: clients waiting for match - enum scan_framework_client */ struct iwm_scan_offload_profile { uint8_t ssid_index; uint8_t unicast_cipher; uint8_t auth_alg; uint8_t network_type; uint8_t band_selection; uint8_t client_bitmap; uint8_t reserved[2]; } __packed; /** * iwm_scan_offload_profile_cfg - IWM_SCAN_OFFLOAD_PROFILES_CFG_API_S_VER_1 * @blaclist: AP list to filter off from scan results * @profiles: profiles to search for match * @blacklist_len: length of blacklist * @num_profiles: num of profiles in the list * @match_notify: clients waiting for match found notification * @pass_match: clients waiting for the results * @active_clients: active clients bitmap - enum scan_framework_client * @any_beacon_notify: clients waiting for match notification without match */ struct iwm_scan_offload_profile_cfg { struct iwm_scan_offload_profile profiles[IWM_SCAN_MAX_PROFILES]; uint8_t blacklist_len; uint8_t num_profiles; uint8_t match_notify; uint8_t pass_match; uint8_t active_clients; uint8_t any_beacon_notify; uint8_t reserved[2]; } __packed; /** * iwm_scan_offload_schedule - schedule of scan offload * @delay: delay between iterations, in seconds. * @iterations: num of scan iterations * @full_scan_mul: number of partial scans before each full scan */ struct iwm_scan_offload_schedule { uint16_t delay; uint8_t iterations; uint8_t full_scan_mul; } __packed; /* * iwm_scan_offload_flags * * IWM_SCAN_OFFLOAD_FLAG_PASS_ALL: pass all results - no filtering. * IWM_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL: add cached channels to partial scan. * IWM_SCAN_OFFLOAD_FLAG_ENERGY_SCAN: use energy based scan before partial scan * on A band. */ enum iwm_scan_offload_flags { IWM_SCAN_OFFLOAD_FLAG_PASS_ALL = (1 << 0), IWM_SCAN_OFFLOAD_FLAG_CACHED_CHANNEL = (1 << 2), IWM_SCAN_OFFLOAD_FLAG_ENERGY_SCAN = (1 << 3), }; /** * iwm_scan_offload_req - scan offload request command * @flags: bitmap - enum iwm_scan_offload_flags. * @watchdog: maximum scan duration in TU. * @delay: delay in seconds before first iteration. * @schedule_line: scan offload schedule, for fast and regular scan. */ struct iwm_scan_offload_req { uint16_t flags; uint16_t watchdog; uint16_t delay; uint16_t reserved; struct iwm_scan_offload_schedule schedule_line[2]; } __packed; enum iwm_scan_offload_compleate_status { IWM_SCAN_OFFLOAD_COMPLETED = 1, IWM_SCAN_OFFLOAD_ABORTED = 2, }; /** + * struct iwm_lmac_scan_complete_notif - notifies end of scanning (all channels) + * SCAN_COMPLETE_NTF_API_S_VER_3 + * @scanned_channels: number of channels scanned (and number of valid results) + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: an array of scan results, only "scanned_channels" of them are valid + */ +struct iwm_lmac_scan_complete_notif { + uint8_t scanned_channels; + uint8_t status; + uint8_t bt_status; + uint8_t last_channel; + uint32_t tsf_low; + uint32_t tsf_high; + struct iwm_scan_results_notif results[]; +} __packed; + + +/** * iwm_scan_offload_complete - IWM_SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1 * @last_schedule_line: last schedule line executed (fast or regular) * @last_schedule_iteration: last scan iteration executed before scan abort * @status: enum iwm_scan_offload_compleate_status */ struct iwm_scan_offload_complete { uint8_t last_schedule_line; uint8_t last_schedule_iteration; uint8_t status; uint8_t reserved; } __packed; /** * iwm_sched_scan_results - IWM_SCAN_OFFLOAD_MATCH_FOUND_NTF_API_S_VER_1 * @ssid_bitmap: SSIDs indexes found in this iteration * @client_bitmap: clients that are active and wait for this notification */ struct iwm_sched_scan_results { uint16_t ssid_bitmap; uint8_t client_bitmap; uint8_t reserved; }; /* * END mvm/fw-api-scan.h */ /* * BEGIN mvm/fw-api-sta.h */ +/* UMAC Scan API */ + +/* The maximum of either of these cannot exceed 8, because we use an + * 8-bit mask (see IWM_MVM_SCAN_MASK). + */ +#define IWM_MVM_MAX_UMAC_SCANS 8 +#define IWM_MVM_MAX_LMAC_SCANS 1 + +enum iwm_scan_config_flags { + IWM_SCAN_CONFIG_FLAG_ACTIVATE = (1 << 0), + IWM_SCAN_CONFIG_FLAG_DEACTIVATE = (1 << 1), + IWM_SCAN_CONFIG_FLAG_FORBID_CHUB_REQS = (1 << 2), + IWM_SCAN_CONFIG_FLAG_ALLOW_CHUB_REQS = (1 << 3), + IWM_SCAN_CONFIG_FLAG_SET_TX_CHAINS = (1 << 8), + IWM_SCAN_CONFIG_FLAG_SET_RX_CHAINS = (1 << 9), + IWM_SCAN_CONFIG_FLAG_SET_AUX_STA_ID = (1 << 10), + IWM_SCAN_CONFIG_FLAG_SET_ALL_TIMES = (1 << 11), + IWM_SCAN_CONFIG_FLAG_SET_EFFECTIVE_TIMES = (1 << 12), + IWM_SCAN_CONFIG_FLAG_SET_CHANNEL_FLAGS = (1 << 13), + IWM_SCAN_CONFIG_FLAG_SET_LEGACY_RATES = (1 << 14), + IWM_SCAN_CONFIG_FLAG_SET_MAC_ADDR = (1 << 15), + IWM_SCAN_CONFIG_FLAG_SET_FRAGMENTED = (1 << 16), + IWM_SCAN_CONFIG_FLAG_CLEAR_FRAGMENTED = (1 << 17), + IWM_SCAN_CONFIG_FLAG_SET_CAM_MODE = (1 << 18), + IWM_SCAN_CONFIG_FLAG_CLEAR_CAM_MODE = (1 << 19), + IWM_SCAN_CONFIG_FLAG_SET_PROMISC_MODE = (1 << 20), + IWM_SCAN_CONFIG_FLAG_CLEAR_PROMISC_MODE = (1 << 21), + + /* Bits 26-31 are for num of channels in channel_array */ +#define IWM_SCAN_CONFIG_N_CHANNELS(n) ((n) << 26) +}; + +enum iwm_scan_config_rates { + /* OFDM basic rates */ + IWM_SCAN_CONFIG_RATE_6M = (1 << 0), + IWM_SCAN_CONFIG_RATE_9M = (1 << 1), + IWM_SCAN_CONFIG_RATE_12M = (1 << 2), + IWM_SCAN_CONFIG_RATE_18M = (1 << 3), + IWM_SCAN_CONFIG_RATE_24M = (1 << 4), + IWM_SCAN_CONFIG_RATE_36M = (1 << 5), + IWM_SCAN_CONFIG_RATE_48M = (1 << 6), + IWM_SCAN_CONFIG_RATE_54M = (1 << 7), + /* CCK basic rates */ + IWM_SCAN_CONFIG_RATE_1M = (1 << 8), + IWM_SCAN_CONFIG_RATE_2M = (1 << 9), + IWM_SCAN_CONFIG_RATE_5M = (1 << 10), + IWM_SCAN_CONFIG_RATE_11M = (1 << 11), + + /* Bits 16-27 are for supported rates */ +#define IWM_SCAN_CONFIG_SUPPORTED_RATE(rate) ((rate) << 16) +}; + +enum iwm_channel_flags { + IWM_CHANNEL_FLAG_EBS = (1 << 0), + IWM_CHANNEL_FLAG_ACCURATE_EBS = (1 << 1), + IWM_CHANNEL_FLAG_EBS_ADD = (1 << 2), + IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE = (1 << 3), +}; + /** + * struct iwm_scan_config + * @flags: enum scan_config_flags + * @tx_chains: valid_tx antenna - ANT_* definitions + * @rx_chains: valid_rx antenna - ANT_* definitions + * @legacy_rates: default legacy rates - enum scan_config_rates + * @out_of_channel_time: default max out of serving channel time + * @suspend_time: default max suspend time + * @dwell_active: default dwell time for active scan + * @dwell_passive: default dwell time for passive scan + * @dwell_fragmented: default dwell time for fragmented scan + * @dwell_extended: default dwell time for channels 1, 6 and 11 + * @mac_addr: default mac address to be used in probes + * @bcast_sta_id: the index of the station in the fw + * @channel_flags: default channel flags - enum iwm_channel_flags + * scan_config_channel_flag + * @channel_array: default supported channels + */ +struct iwm_scan_config { + uint32_t flags; + uint32_t tx_chains; + uint32_t rx_chains; + uint32_t legacy_rates; + uint32_t out_of_channel_time; + uint32_t suspend_time; + uint8_t dwell_active; + uint8_t dwell_passive; + uint8_t dwell_fragmented; + uint8_t dwell_extended; + uint8_t mac_addr[IEEE80211_ADDR_LEN]; + uint8_t bcast_sta_id; + uint8_t channel_flags; + uint8_t channel_array[]; +} __packed; /* SCAN_CONFIG_DB_CMD_API_S */ + +/** + * iwm_umac_scan_flags + *@IWM_UMAC_SCAN_FLAG_PREEMPTIVE: scan process triggered by this scan request + * can be preempted by other scan requests with higher priority. + * The low priority scan will be resumed when the higher proirity scan is + * completed. + *@IWM_UMAC_SCAN_FLAG_START_NOTIF: notification will be sent to the driver + * when scan starts. + */ +enum iwm_umac_scan_flags { + IWM_UMAC_SCAN_FLAG_PREEMPTIVE = (1 << 0), + IWM_UMAC_SCAN_FLAG_START_NOTIF = (1 << 1), +}; + +enum iwm_umac_scan_uid_offsets { + IWM_UMAC_SCAN_UID_TYPE_OFFSET = 0, + IWM_UMAC_SCAN_UID_SEQ_OFFSET = 8, +}; + +enum iwm_umac_scan_general_flags { + IWM_UMAC_SCAN_GEN_FLAGS_PERIODIC = (1 << 0), + IWM_UMAC_SCAN_GEN_FLAGS_OVER_BT = (1 << 1), + IWM_UMAC_SCAN_GEN_FLAGS_PASS_ALL = (1 << 2), + IWM_UMAC_SCAN_GEN_FLAGS_PASSIVE = (1 << 3), + IWM_UMAC_SCAN_GEN_FLAGS_PRE_CONNECT = (1 << 4), + IWM_UMAC_SCAN_GEN_FLAGS_ITER_COMPLETE = (1 << 5), + IWM_UMAC_SCAN_GEN_FLAGS_MULTIPLE_SSID = (1 << 6), + IWM_UMAC_SCAN_GEN_FLAGS_FRAGMENTED = (1 << 7), + IWM_UMAC_SCAN_GEN_FLAGS_RRM_ENABLED = (1 << 8), + IWM_UMAC_SCAN_GEN_FLAGS_MATCH = (1 << 9), + IWM_UMAC_SCAN_GEN_FLAGS_EXTENDED_DWELL = (1 << 10), +}; + +/** + * struct iwm_scan_channel_cfg_umac + * @flags: bitmap - 0-19: directed scan to i'th ssid. + * @channel_num: channel number 1-13 etc. + * @iter_count: repetition count for the channel. + * @iter_interval: interval between two scan iterations on one channel. + */ +struct iwm_scan_channel_cfg_umac { + uint32_t flags; + uint8_t channel_num; + uint8_t iter_count; + uint16_t iter_interval; +} __packed; /* SCAN_CHANNEL_CFG_S_VER2 */ + +/** + * struct iwm_scan_umac_schedule + * @interval: interval in seconds between scan iterations + * @iter_count: num of scan iterations for schedule plan, 0xff for infinite loop + * @reserved: for alignment and future use + */ +struct iwm_scan_umac_schedule { + uint16_t interval; + uint8_t iter_count; + uint8_t reserved; +} __packed; /* SCAN_SCHED_PARAM_API_S_VER_1 */ + +/** + * struct iwm_scan_req_umac_tail - the rest of the UMAC scan request command + * parameters following channels configuration array. + * @schedule: two scheduling plans. + * @delay: delay in TUs before starting the first scan iteration + * @reserved: for future use and alignment + * @preq: probe request with IEs blocks + * @direct_scan: list of SSIDs for directed active scan + */ +struct iwm_scan_req_umac_tail { + /* SCAN_PERIODIC_PARAMS_API_S_VER_1 */ + struct iwm_scan_umac_schedule schedule[IWM_MAX_SCHED_SCAN_PLANS]; + uint16_t delay; + uint16_t reserved; + /* SCAN_PROBE_PARAMS_API_S_VER_1 */ + struct iwm_scan_probe_req preq; + struct iwm_ssid_ie direct_scan[IWM_PROBE_OPTION_MAX]; +} __packed; + +/** + * struct iwm_scan_req_umac + * @flags: &enum iwm_umac_scan_flags + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @ooc_priority: out of channel priority - &enum iwm_scan_priority + * @general_flags: &enum iwm_umac_scan_general_flags + * @extended_dwell: dwell time for channels 1, 6 and 11 + * @active_dwell: dwell time for active scan + * @passive_dwell: dwell time for passive scan + * @fragmented_dwell: dwell time for fragmented passive scan + * @max_out_time: max out of serving channel time + * @suspend_time: max suspend time + * @scan_priority: scan internal prioritization &enum iwm_scan_priority + * @channel_flags: &enum iwm_scan_channel_flags + * @n_channels: num of channels in scan request + * @reserved: for future use and alignment + * @data: &struct iwm_scan_channel_cfg_umac and + * &struct iwm_scan_req_umac_tail + */ +struct iwm_scan_req_umac { + uint32_t flags; + uint32_t uid; + uint32_t ooc_priority; + /* SCAN_GENERAL_PARAMS_API_S_VER_1 */ + uint32_t general_flags; + uint8_t extended_dwell; + uint8_t active_dwell; + uint8_t passive_dwell; + uint8_t fragmented_dwell; + uint32_t max_out_time; + uint32_t suspend_time; + uint32_t scan_priority; + /* SCAN_CHANNEL_PARAMS_API_S_VER_1 */ + uint8_t channel_flags; + uint8_t n_channels; + uint16_t reserved; + uint8_t data[]; +} __packed; /* SCAN_REQUEST_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwm_umac_scan_abort + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @flags: reserved + */ +struct iwm_umac_scan_abort { + uint32_t uid; + uint32_t flags; +} __packed; /* SCAN_ABORT_CMD_UMAC_API_S_VER_1 */ + +/** + * struct iwm_umac_scan_complete + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @last_schedule: last scheduling line + * @last_iter: last scan iteration number + * @scan status: &enum iwm_scan_offload_complete_status + * @ebs_status: &enum iwm_scan_ebs_status + * @time_from_last_iter: time elapsed from last iteration + * @reserved: for future use + */ +struct iwm_umac_scan_complete { + uint32_t uid; + uint8_t last_schedule; + uint8_t last_iter; + uint8_t status; + uint8_t ebs_status; + uint32_t time_from_last_iter; + uint32_t reserved; +} __packed; /* SCAN_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +#define IWM_SCAN_OFFLOAD_MATCHING_CHANNELS_LEN 5 +/** + * struct iwm_scan_offload_profile_match - match information + * @bssid: matched bssid + * @channel: channel where the match occurred + * @energy: + * @matching_feature: + * @matching_channels: bitmap of channels that matched, referencing + * the channels passed in tue scan offload request + */ +struct iwm_scan_offload_profile_match { + uint8_t bssid[IEEE80211_ADDR_LEN]; + uint16_t reserved; + uint8_t channel; + uint8_t energy; + uint8_t matching_feature; + uint8_t matching_channels[IWM_SCAN_OFFLOAD_MATCHING_CHANNELS_LEN]; +} __packed; /* SCAN_OFFLOAD_PROFILE_MATCH_RESULTS_S_VER_1 */ + +/** + * struct iwm_scan_offload_profiles_query - match results query response + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @last_scan_age: age of the last offloaded scan + * @n_scans_done: number of offloaded scans done + * @gp2_d0u: GP2 when D0U occurred + * @gp2_invoked: GP2 when scan offload was invoked + * @resume_while_scanning: not used + * @self_recovery: obsolete + * @reserved: reserved + * @matches: array of match information, one for each match + */ +struct iwm_scan_offload_profiles_query { + uint32_t matched_profiles; + uint32_t last_scan_age; + uint32_t n_scans_done; + uint32_t gp2_d0u; + uint32_t gp2_invoked; + uint8_t resume_while_scanning; + uint8_t self_recovery; + uint16_t reserved; + struct iwm_scan_offload_profile_match matches[IWM_SCAN_MAX_PROFILES]; +} __packed; /* SCAN_OFFLOAD_PROFILES_QUERY_RSP_S_VER_2 */ + +/** + * struct iwm_umac_scan_iter_complete_notif - notifies end of scanning iteration + * @uid: scan id, &enum iwm_umac_scan_uid_offsets + * @scanned_channels: number of channels scanned and number of valid elements in + * results array + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: array of scan results, only "scanned_channels" of them are valid + */ +struct iwm_umac_scan_iter_complete_notif { + uint32_t uid; + uint8_t scanned_channels; + uint8_t status; + uint8_t bt_status; + uint8_t last_channel; + uint32_t tsf_low; + uint32_t tsf_high; + struct iwm_scan_results_notif results[]; +} __packed; /* SCAN_ITER_COMPLETE_NTF_UMAC_API_S_VER_1 */ + +/* Please keep this enum *SORTED* by hex value. + * Needed for binary search, otherwise a warning will be triggered. + */ +enum iwm_scan_subcmd_ids { + IWM_GSCAN_START_CMD = 0x0, + IWM_GSCAN_STOP_CMD = 0x1, + IWM_GSCAN_SET_HOTLIST_CMD = 0x2, + IWM_GSCAN_RESET_HOTLIST_CMD = 0x3, + IWM_GSCAN_SET_SIGNIFICANT_CHANGE_CMD = 0x4, + IWM_GSCAN_RESET_SIGNIFICANT_CHANGE_CMD = 0x5, + IWM_GSCAN_SIGNIFICANT_CHANGE_EVENT = 0xFD, + IWM_GSCAN_HOTLIST_CHANGE_EVENT = 0xFE, + IWM_GSCAN_RESULTS_AVAILABLE_EVENT = 0xFF, +}; + +/* STA API */ + +/** * enum iwm_sta_flags - flags for the ADD_STA host command * @IWM_STA_FLG_REDUCED_TX_PWR_CTRL: * @IWM_STA_FLG_REDUCED_TX_PWR_DATA: - * @IWM_STA_FLG_FLG_ANT_MSK: Antenna selection + * @IWM_STA_FLG_DISABLE_TX: set if TX should be disabled * @IWM_STA_FLG_PS: set if STA is in Power Save * @IWM_STA_FLG_INVALID: set if STA is invalid * @IWM_STA_FLG_DLP_EN: Direct Link Protocol is enabled * @IWM_STA_FLG_SET_ALL_KEYS: the current key applies to all key IDs * @IWM_STA_FLG_DRAIN_FLOW: drain flow * @IWM_STA_FLG_PAN: STA is for PAN interface * @IWM_STA_FLG_CLASS_AUTH: * @IWM_STA_FLG_CLASS_ASSOC: * @IWM_STA_FLG_CLASS_MIMO_PROT: * @IWM_STA_FLG_MAX_AGG_SIZE_MSK: maximal size for A-MPDU * @IWM_STA_FLG_AGG_MPDU_DENS_MSK: maximal MPDU density for Tx aggregation * @IWM_STA_FLG_FAT_EN_MSK: support for channel width (for Tx). This flag is * initialised by driver and can be updated by fw upon reception of * action frames that can change the channel width. When cleared the fw * will send all the frames in 20MHz even when FAT channel is requested. * @IWM_STA_FLG_MIMO_EN_MSK: support for MIMO. This flag is initialised by the * driver and can be updated by fw upon reception of action frames. * @IWM_STA_FLG_MFP_EN: Management Frame Protection */ enum iwm_sta_flags { IWM_STA_FLG_REDUCED_TX_PWR_CTRL = (1 << 3), IWM_STA_FLG_REDUCED_TX_PWR_DATA = (1 << 6), - IWM_STA_FLG_FLG_ANT_A = (1 << 4), - IWM_STA_FLG_FLG_ANT_B = (2 << 4), - IWM_STA_FLG_FLG_ANT_MSK = (IWM_STA_FLG_FLG_ANT_A | - IWM_STA_FLG_FLG_ANT_B), + IWM_STA_FLG_DISABLE_TX = (1 << 4), IWM_STA_FLG_PS = (1 << 8), IWM_STA_FLG_DRAIN_FLOW = (1 << 12), IWM_STA_FLG_PAN = (1 << 13), IWM_STA_FLG_CLASS_AUTH = (1 << 14), IWM_STA_FLG_CLASS_ASSOC = (1 << 15), IWM_STA_FLG_RTS_MIMO_PROT = (1 << 17), IWM_STA_FLG_MAX_AGG_SIZE_SHIFT = 19, IWM_STA_FLG_MAX_AGG_SIZE_8K = (0 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_16K = (1 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_32K = (2 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_64K = (3 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_128K = (4 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_256K = (5 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_512K = (6 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_1024K = (7 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_MAX_AGG_SIZE_MSK = (7 << IWM_STA_FLG_MAX_AGG_SIZE_SHIFT), IWM_STA_FLG_AGG_MPDU_DENS_SHIFT = 23, IWM_STA_FLG_AGG_MPDU_DENS_2US = (4 << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT), IWM_STA_FLG_AGG_MPDU_DENS_4US = (5 << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT), IWM_STA_FLG_AGG_MPDU_DENS_8US = (6 << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT), IWM_STA_FLG_AGG_MPDU_DENS_16US = (7 << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT), IWM_STA_FLG_AGG_MPDU_DENS_MSK = (7 << IWM_STA_FLG_AGG_MPDU_DENS_SHIFT), IWM_STA_FLG_FAT_EN_20MHZ = (0 << 26), IWM_STA_FLG_FAT_EN_40MHZ = (1 << 26), IWM_STA_FLG_FAT_EN_80MHZ = (2 << 26), IWM_STA_FLG_FAT_EN_160MHZ = (3 << 26), IWM_STA_FLG_FAT_EN_MSK = (3 << 26), IWM_STA_FLG_MIMO_EN_SISO = (0 << 28), IWM_STA_FLG_MIMO_EN_MIMO2 = (1 << 28), IWM_STA_FLG_MIMO_EN_MIMO3 = (2 << 28), IWM_STA_FLG_MIMO_EN_MSK = (3 << 28), }; /** * enum iwm_sta_key_flag - key flags for the ADD_STA host command * @IWM_STA_KEY_FLG_NO_ENC: no encryption * @IWM_STA_KEY_FLG_WEP: WEP encryption algorithm * @IWM_STA_KEY_FLG_CCM: CCMP encryption algorithm * @IWM_STA_KEY_FLG_TKIP: TKIP encryption algorithm * @IWM_STA_KEY_FLG_EXT: extended cipher algorithm (depends on the FW support) * @IWM_STA_KEY_FLG_CMAC: CMAC encryption algorithm * @IWM_STA_KEY_FLG_ENC_UNKNOWN: unknown encryption algorithm * @IWM_STA_KEY_FLG_EN_MSK: mask for encryption algorithmi value * @IWM_STA_KEY_FLG_WEP_KEY_MAP: wep is either a group key (0 - legacy WEP) or from * station info array (1 - n 1X mode) * @IWM_STA_KEY_FLG_KEYID_MSK: the index of the key * @IWM_STA_KEY_NOT_VALID: key is invalid * @IWM_STA_KEY_FLG_WEP_13BYTES: set for 13 bytes WEP key * @IWM_STA_KEY_MULTICAST: set for multical key * @IWM_STA_KEY_MFP: key is used for Management Frame Protection */ enum iwm_sta_key_flag { IWM_STA_KEY_FLG_NO_ENC = (0 << 0), IWM_STA_KEY_FLG_WEP = (1 << 0), IWM_STA_KEY_FLG_CCM = (2 << 0), IWM_STA_KEY_FLG_TKIP = (3 << 0), IWM_STA_KEY_FLG_EXT = (4 << 0), IWM_STA_KEY_FLG_CMAC = (6 << 0), IWM_STA_KEY_FLG_ENC_UNKNOWN = (7 << 0), IWM_STA_KEY_FLG_EN_MSK = (7 << 0), IWM_STA_KEY_FLG_WEP_KEY_MAP = (1 << 3), IWM_STA_KEY_FLG_KEYID_POS = 8, IWM_STA_KEY_FLG_KEYID_MSK = (3 << IWM_STA_KEY_FLG_KEYID_POS), IWM_STA_KEY_NOT_VALID = (1 << 11), IWM_STA_KEY_FLG_WEP_13BYTES = (1 << 12), IWM_STA_KEY_MULTICAST = (1 << 14), IWM_STA_KEY_MFP = (1 << 15), }; /** * enum iwm_sta_modify_flag - indicate to the fw what flag are being changed - * @IWM_STA_MODIFY_KEY: this command modifies %key + * @IWM_STA_MODIFY_QUEUE_REMOVAL: this command removes a queue * @IWM_STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx * @IWM_STA_MODIFY_TX_RATE: unused * @IWM_STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid * @IWM_STA_MODIFY_REMOVE_BA_TID: this command modifies %remove_immediate_ba_tid * @IWM_STA_MODIFY_SLEEPING_STA_TX_COUNT: this command modifies %sleep_tx_count * @IWM_STA_MODIFY_PROT_TH: * @IWM_STA_MODIFY_QUEUES: modify the queues used by this station */ enum iwm_sta_modify_flag { - IWM_STA_MODIFY_KEY = (1 << 0), + IWM_STA_MODIFY_QUEUE_REMOVAL = (1 << 0), IWM_STA_MODIFY_TID_DISABLE_TX = (1 << 1), IWM_STA_MODIFY_TX_RATE = (1 << 2), IWM_STA_MODIFY_ADD_BA_TID = (1 << 3), IWM_STA_MODIFY_REMOVE_BA_TID = (1 << 4), IWM_STA_MODIFY_SLEEPING_STA_TX_COUNT = (1 << 5), IWM_STA_MODIFY_PROT_TH = (1 << 6), IWM_STA_MODIFY_QUEUES = (1 << 7), }; #define IWM_STA_MODE_MODIFY 1 /** * enum iwm_sta_sleep_flag - type of sleep of the station * @IWM_STA_SLEEP_STATE_AWAKE: * @IWM_STA_SLEEP_STATE_PS_POLL: * @IWM_STA_SLEEP_STATE_UAPSD: + * @IWM_STA_SLEEP_STATE_MOREDATA: set more-data bit on + * (last) released frame */ enum iwm_sta_sleep_flag { IWM_STA_SLEEP_STATE_AWAKE = 0, IWM_STA_SLEEP_STATE_PS_POLL = (1 << 0), IWM_STA_SLEEP_STATE_UAPSD = (1 << 1), + IWM_STA_SLEEP_STATE_MOREDATA = (1 << 2), }; /* STA ID and color bits definitions */ #define IWM_STA_ID_SEED (0x0f) #define IWM_STA_ID_POS (0) #define IWM_STA_ID_MSK (IWM_STA_ID_SEED << IWM_STA_ID_POS) #define IWM_STA_COLOR_SEED (0x7) #define IWM_STA_COLOR_POS (4) #define IWM_STA_COLOR_MSK (IWM_STA_COLOR_SEED << IWM_STA_COLOR_POS) #define IWM_STA_ID_N_COLOR_GET_COLOR(id_n_color) \ (((id_n_color) & IWM_STA_COLOR_MSK) >> IWM_STA_COLOR_POS) #define IWM_STA_ID_N_COLOR_GET_ID(id_n_color) \ (((id_n_color) & IWM_STA_ID_MSK) >> IWM_STA_ID_POS) #define IWM_STA_KEY_MAX_NUM (16) #define IWM_STA_KEY_IDX_INVALID (0xff) #define IWM_STA_KEY_MAX_DATA_KEY_NUM (4) #define IWM_MAX_GLOBAL_KEYS (4) #define IWM_STA_KEY_LEN_WEP40 (5) #define IWM_STA_KEY_LEN_WEP104 (13) /** * struct iwm_mvm_keyinfo - key information * @key_flags: type %iwm_sta_key_flag * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx * @key_offset: key offset in the fw's key table * @key: 16-byte unicast decryption key * @tx_secur_seq_cnt: initial RSC / PN needed for replay check * @hw_tkip_mic_rx_key: byte: MIC Rx Key - used for TKIP only * @hw_tkip_mic_tx_key: byte: MIC Tx Key - used for TKIP only */ struct iwm_mvm_keyinfo { uint16_t key_flags; uint8_t tkip_rx_tsc_byte2; uint8_t reserved1; uint16_t tkip_rx_ttak[5]; uint8_t key_offset; uint8_t reserved2; uint8_t key[16]; uint64_t tx_secur_seq_cnt; uint64_t hw_tkip_mic_rx_key; uint64_t hw_tkip_mic_tx_key; } __packed; +#define IWM_ADD_STA_STATUS_MASK 0xFF +#define IWM_ADD_STA_BAID_VALID_MASK 0x8000 +#define IWM_ADD_STA_BAID_MASK 0x7F00 +#define IWM_ADD_STA_BAID_SHIFT 8 + /** - * struct iwm_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table. - * ( IWM_REPLY_ADD_STA = 0x18 ) + * struct iwm_mvm_add_sta_cmd_v7 - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) * @add_modify: 1: modify existing, 0: add new station - * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent - * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key - * sent + * @awake_acs: + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %IWM_STA_MODIFY_TID_DISABLE_TX to change this field. * @mac_id_n_color: the Mac context this station belongs to * @addr[IEEE80211_ADDR_LEN]: station's MAC address * @sta_id: index of station in uCode's station table * @modify_mask: IWM_STA_MODIFY_*, selects which parameters to modify vs. leave * alone. 1 - modify, 0 - don't change. - * @key: look at %iwm_mvm_keyinfo * @station_flags: look at %iwm_sta_flags * @station_flags_msk: what of %station_flags have changed - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %IWM_STA_MODIFY_TID_DISABLE_TX to change this field. * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) * Set %IWM_STA_MODIFY_ADD_BA_TID to use this field, and also set * add_immediate_ba_ssn. * @remove_immediate_ba_tid: tid for which to remove block-ack support (Rx) * Set %IWM_STA_MODIFY_REMOVE_BA_TID to use this field * @add_immediate_ba_ssn: ssn for the Rx block-ack session. Used together with * add_immediate_ba_tid. * @sleep_tx_count: number of packets to transmit to station even though it is * asleep. Used to synchronise PS-poll and u-APSD responses while ucode * keeps track of STA sleep state. * @sleep_state_flags: Look at %iwm_sta_sleep_flag. * @assoc_id: assoc_id to be sent in VHT PLCP (9-bit), for grp use 0, for AP * mac-addr. * @beamform_flags: beam forming controls * @tfd_queue_msk: tfd queues used by this station * * The device contains an internal table of per-station information, with info * on security keys, aggregation parameters, and Tx rates for initial Tx * attempt and any retries (set by IWM_REPLY_TX_LINK_QUALITY_CMD). * * ADD_STA sets up the table entry for one station, either creating a new * entry, or modifying a pre-existing one. */ -struct iwm_mvm_add_sta_cmd_v5 { +struct iwm_mvm_add_sta_cmd_v7 { uint8_t add_modify; - uint8_t unicast_tx_key_id; - uint8_t multicast_tx_key_id; - uint8_t reserved1; - uint32_t mac_id_n_color; - uint8_t addr[IEEE80211_ADDR_LEN]; - uint16_t reserved2; - uint8_t sta_id; - uint8_t modify_mask; - uint16_t reserved3; - struct iwm_mvm_keyinfo key; - uint32_t station_flags; - uint32_t station_flags_msk; + uint8_t awake_acs; uint16_t tid_disable_tx; - uint16_t reserved4; - uint8_t add_immediate_ba_tid; - uint8_t remove_immediate_ba_tid; - uint16_t add_immediate_ba_ssn; - uint16_t sleep_tx_count; - uint16_t sleep_state_flags; - uint16_t assoc_id; - uint16_t beamform_flags; - uint32_t tfd_queue_msk; -} __packed; /* IWM_ADD_STA_CMD_API_S_VER_5 */ - -/** - * struct iwm_mvm_add_sta_cmd_v6 - Add / modify a station - * VER_6 of this command is quite similar to VER_5 except - * exclusion of all fields related to the security key installation. - */ -struct iwm_mvm_add_sta_cmd_v6 { - uint8_t add_modify; - uint8_t reserved1; - uint16_t tid_disable_tx; uint32_t mac_id_n_color; uint8_t addr[IEEE80211_ADDR_LEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ uint16_t reserved2; uint8_t sta_id; uint8_t modify_mask; uint16_t reserved3; uint32_t station_flags; uint32_t station_flags_msk; uint8_t add_immediate_ba_tid; uint8_t remove_immediate_ba_tid; uint16_t add_immediate_ba_ssn; uint16_t sleep_tx_count; uint16_t sleep_state_flags; uint16_t assoc_id; uint16_t beamform_flags; uint32_t tfd_queue_msk; -} __packed; /* IWM_ADD_STA_CMD_API_S_VER_6 */ +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ /** * struct iwm_mvm_add_sta_key_cmd - add/modify sta key * ( IWM_REPLY_ADD_STA_KEY = 0x17 ) * @sta_id: index of station in uCode's station table * @key_offset: key offset in key storage * @key_flags: type %iwm_sta_key_flag * @key: key material data * @key2: key material data * @rx_secur_seq_cnt: RX security sequence counter for the key * @tkip_rx_tsc_byte2: TSC[2] for key mix ph1 detection * @tkip_rx_ttak: 10-byte unicast TKIP TTAK for Rx */ struct iwm_mvm_add_sta_key_cmd { uint8_t sta_id; uint8_t key_offset; uint16_t key_flags; uint8_t key[16]; uint8_t key2[16]; uint8_t rx_secur_seq_cnt[16]; uint8_t tkip_rx_tsc_byte2; uint8_t reserved; uint16_t tkip_rx_ttak[5]; } __packed; /* IWM_ADD_MODIFY_STA_KEY_API_S_VER_1 */ /** * enum iwm_mvm_add_sta_rsp_status - status in the response to ADD_STA command * @IWM_ADD_STA_SUCCESS: operation was executed successfully * @IWM_ADD_STA_STATIONS_OVERLOAD: no room left in the fw's station table * @IWM_ADD_STA_IMMEDIATE_BA_FAILURE: can't add Rx block ack session * @IWM_ADD_STA_MODIFY_NON_EXISTING_STA: driver requested to modify a station * that doesn't exist. */ enum iwm_mvm_add_sta_rsp_status { IWM_ADD_STA_SUCCESS = 0x1, IWM_ADD_STA_STATIONS_OVERLOAD = 0x2, IWM_ADD_STA_IMMEDIATE_BA_FAILURE = 0x4, IWM_ADD_STA_MODIFY_NON_EXISTING_STA = 0x8, }; /** * struct iwm_mvm_rm_sta_cmd - Add / modify a station in the fw's station table * ( IWM_REMOVE_STA = 0x19 ) * @sta_id: the station id of the station to be removed */ struct iwm_mvm_rm_sta_cmd { uint8_t sta_id; uint8_t reserved[3]; } __packed; /* IWM_REMOVE_STA_CMD_API_S_VER_2 */ /** * struct iwm_mvm_mgmt_mcast_key_cmd * ( IWM_MGMT_MCAST_KEY = 0x1f ) * @ctrl_flags: %iwm_sta_key_flag * @IGTK: * @K1: IGTK master key * @K2: IGTK sub key * @sta_id: station ID that support IGTK * @key_id: * @receive_seq_cnt: initial RSC/PN needed for replay check */ struct iwm_mvm_mgmt_mcast_key_cmd { uint32_t ctrl_flags; uint8_t IGTK[16]; uint8_t K1[16]; uint8_t K2[16]; uint32_t key_id; uint32_t sta_id; uint64_t receive_seq_cnt; } __packed; /* SEC_MGMT_MULTICAST_KEY_CMD_API_S_VER_1 */ struct iwm_mvm_wep_key { uint8_t key_index; uint8_t key_offset; uint16_t reserved1; uint8_t key_size; uint8_t reserved2[3]; uint8_t key[16]; } __packed; struct iwm_mvm_wep_key_cmd { uint32_t mac_id_n_color; uint8_t num_keys; uint8_t decryption_type; uint8_t flags; uint8_t reserved; struct iwm_mvm_wep_key wep_key[0]; } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ - /* * END mvm/fw-api-sta.h */ /* + * BT coex + */ + +enum iwm_bt_coex_mode { + IWM_BT_COEX_DISABLE = 0x0, + IWM_BT_COEX_NW = 0x1, + IWM_BT_COEX_BT = 0x2, + IWM_BT_COEX_WIFI = 0x3, +}; /* BT_COEX_MODES_E */ + +enum iwm_bt_coex_enabled_modules { + IWM_BT_COEX_MPLUT_ENABLED = (1 << 0), + IWM_BT_COEX_MPLUT_BOOST_ENABLED = (1 << 1), + IWM_BT_COEX_SYNC2SCO_ENABLED = (1 << 2), + IWM_BT_COEX_CORUN_ENABLED = (1 << 3), + IWM_BT_COEX_HIGH_BAND_RET = (1 << 4), +}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ + +/** + * struct iwm_bt_coex_cmd - bt coex configuration command + * @mode: enum %iwm_bt_coex_mode + * @enabled_modules: enum %iwm_bt_coex_enabled_modules + * + * The structure is used for the BT_COEX command. + */ +struct iwm_bt_coex_cmd { + uint32_t mode; + uint32_t enabled_modules; +} __packed; /* BT_COEX_CMD_API_S_VER_6 */ + + +/* + * Location Aware Regulatory (LAR) API - MCC updates + */ + +/** + * struct iwm_mcc_update_cmd_v1 - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwm_mcc_source + * @reserved: reserved for alignment + */ +struct iwm_mcc_update_cmd_v1 { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ + +/** + * struct iwm_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwm_mcc_source + * @reserved: reserved for alignment + * @key: integrity key for MCC API OEM testing + * @reserved2: reserved + */ +struct iwm_mcc_update_cmd { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved; + uint32_t key; + uint32_t reserved2[5]; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ + +/** + * iwm_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwm_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwm_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwm_mcc_update_resp_v1 { + uint32_t status; + uint16_t mcc; + uint8_t cap; + uint8_t source_id; + uint32_t n_channels; + uint32_t channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ + +/** + * iwm_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwm_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwm_mcc_source + * @time: time elapsed from the MCC test start (in 30 seconds TU) + * @reserved: reserved. + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwm_mcc_update_resp { + uint32_t status; + uint16_t mcc; + uint8_t cap; + uint8_t source_id; + uint16_t time; + uint16_t reserved; + uint32_t n_channels; + uint32_t channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ + +/** + * struct iwm_mcc_chub_notif - chub notifies of mcc change + * (MCC_CHUB_UPDATE_CMD = 0xc9) + * The Chub (Communication Hub, CommsHUB) is a HW component that connects to + * the cellular and connectivity cores that gets updates of the mcc, and + * notifies the ucode directly of any mcc change. + * The ucode requests the driver to request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: identity of the change originator, see iwm_mcc_source + * @reserved1: reserved for alignment + */ +struct iwm_mcc_chub_notif { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved1; +} __packed; /* LAR_MCC_NOTIFY_S */ + +enum iwm_mcc_update_status { + IWM_MCC_RESP_NEW_CHAN_PROFILE, + IWM_MCC_RESP_SAME_CHAN_PROFILE, + IWM_MCC_RESP_INVALID, + IWM_MCC_RESP_NVM_DISABLED, + IWM_MCC_RESP_ILLEGAL, + IWM_MCC_RESP_LOW_PRIORITY, + IWM_MCC_RESP_TEST_MODE_ACTIVE, + IWM_MCC_RESP_TEST_MODE_NOT_ACTIVE, + IWM_MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, +}; + +enum iwm_mcc_source { + IWM_MCC_SOURCE_OLD_FW = 0, + IWM_MCC_SOURCE_ME = 1, + IWM_MCC_SOURCE_BIOS = 2, + IWM_MCC_SOURCE_3G_LTE_HOST = 3, + IWM_MCC_SOURCE_3G_LTE_DEVICE = 4, + IWM_MCC_SOURCE_WIFI = 5, + IWM_MCC_SOURCE_RESERVED = 6, + IWM_MCC_SOURCE_DEFAULT = 7, + IWM_MCC_SOURCE_UNINITIALIZED = 8, + IWM_MCC_SOURCE_MCC_API = 9, + IWM_MCC_SOURCE_GET_CURRENT = 0x10, + IWM_MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, +}; + +/* * Some cherry-picked definitions */ #define IWM_FRAME_LIMIT 64 +/* + * From Linux commit ab02165ccec4c78162501acedeef1a768acdb811: + * As the firmware is slowly running out of command IDs and grouping of + * commands is desirable anyway, the firmware is extending the command + * header from 4 bytes to 8 bytes to introduce a group (in place of the + * former flags field, since that's always 0 on commands and thus can + * be easily used to distinguish between the two). + * + * These functions retrieve specific information from the id field in + * the iwm_host_cmd struct which contains the command id, the group id, + * and the version of the command. +*/ +static inline uint8_t +iwm_cmd_opcode(uint32_t cmdid) +{ + return cmdid & 0xff; +} + +static inline uint8_t +iwm_cmd_groupid(uint32_t cmdid) +{ + return ((cmdid & 0Xff00) >> 8); +} + +static inline uint8_t +iwm_cmd_version(uint32_t cmdid) +{ + return ((cmdid & 0xff0000) >> 16); +} + +static inline uint32_t +iwm_cmd_id(uint8_t opcode, uint8_t groupid, uint8_t version) +{ + return opcode + (groupid << 8) + (version << 16); +} + +/* make uint16_t wide id out of uint8_t group and opcode */ +#define IWM_WIDE_ID(grp, opcode) ((grp << 8) | opcode) + +/* due to the conversion, this group is special */ +#define IWM_ALWAYS_LONG_GROUP 1 + struct iwm_cmd_header { uint8_t code; uint8_t flags; uint8_t idx; uint8_t qid; } __packed; +struct iwm_cmd_header_wide { + uint8_t opcode; + uint8_t group_id; + uint8_t idx; + uint8_t qid; + uint16_t length; + uint8_t reserved; + uint8_t version; +} __packed; + enum iwm_power_scheme { IWM_POWER_SCHEME_CAM = 1, IWM_POWER_SCHEME_BPS, IWM_POWER_SCHEME_LP }; #define IWM_DEF_CMD_PAYLOAD_SIZE 320 #define IWM_MAX_CMD_PAYLOAD_SIZE ((4096 - 4) - sizeof(struct iwm_cmd_header)) #define IWM_CMD_FAILED_MSK 0x40 +/** + * struct iwm_device_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for commands that + * aren't fully copied and use other TFD space. + */ struct iwm_device_cmd { - struct iwm_cmd_header hdr; - - uint8_t data[IWM_DEF_CMD_PAYLOAD_SIZE]; + union { + struct { + struct iwm_cmd_header hdr; + uint8_t data[IWM_DEF_CMD_PAYLOAD_SIZE]; + }; + struct { + struct iwm_cmd_header_wide hdr_wide; + uint8_t data_wide[IWM_DEF_CMD_PAYLOAD_SIZE - + sizeof(struct iwm_cmd_header_wide) + + sizeof(struct iwm_cmd_header)]; + }; + }; } __packed; struct iwm_rx_packet { /* * The first 4 bytes of the RX frame header contain both the RX frame * size and some flags. * Bit fields: * 31: flag flush RB request * 30: flag ignore TC (terminal counter) request * 29: flag fast IRQ request * 28-14: Reserved * 13-00: RX frame size */ uint32_t len_n_flags; struct iwm_cmd_header hdr; uint8_t data[]; } __packed; #define IWM_FH_RSCSR_FRAME_SIZE_MSK 0x00003fff static inline uint32_t iwm_rx_packet_len(const struct iwm_rx_packet *pkt) { return le32toh(pkt->len_n_flags) & IWM_FH_RSCSR_FRAME_SIZE_MSK; } static inline uint32_t iwm_rx_packet_payload_len(const struct iwm_rx_packet *pkt) { return iwm_rx_packet_len(pkt) - sizeof(pkt->hdr); } #define IWM_MIN_DBM -100 #define IWM_MAX_DBM -33 /* realistic guess */ #define IWM_READ(sc, reg) \ bus_space_read_4((sc)->sc_st, (sc)->sc_sh, (reg)) #define IWM_WRITE(sc, reg, val) \ bus_space_write_4((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define IWM_WRITE_1(sc, reg, val) \ bus_space_write_1((sc)->sc_st, (sc)->sc_sh, (reg), (val)) #define IWM_SETBITS(sc, reg, mask) \ IWM_WRITE(sc, reg, IWM_READ(sc, reg) | (mask)) #define IWM_CLRBITS(sc, reg, mask) \ IWM_WRITE(sc, reg, IWM_READ(sc, reg) & ~(mask)) #define IWM_BARRIER_WRITE(sc) \ bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ BUS_SPACE_BARRIER_WRITE) #define IWM_BARRIER_READ_WRITE(sc) \ bus_space_barrier((sc)->sc_st, (sc)->sc_sh, 0, (sc)->sc_sz, \ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) - -#define IWM_FW_VALID_TX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \ - >> IWM_FW_PHY_CFG_TX_CHAIN_POS) -#define IWM_FW_VALID_RX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \ - >> IWM_FW_PHY_CFG_RX_CHAIN_POS) #endif /* __IF_IWM_REG_H__ */ Index: head/sys/dev/iwm/if_iwmvar.h =================================================================== --- head/sys/dev/iwm/if_iwmvar.h (revision 303326) +++ head/sys/dev/iwm/if_iwmvar.h (revision 303327) @@ -1,526 +1,534 @@ /* $OpenBSD: if_iwmvar.h,v 1.7 2015/03/02 13:51:10 jsg Exp $ */ /* $FreeBSD$ */ /* * Copyright (c) 2014 genua mbh * 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 * 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 * * 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. */ struct iwm_rx_radiotap_header { struct ieee80211_radiotap_header wr_ihdr; uint64_t wr_tsft; uint8_t wr_flags; uint8_t wr_rate; uint16_t wr_chan_freq; uint16_t wr_chan_flags; int8_t wr_dbm_antsignal; int8_t wr_dbm_antnoise; } __packed; #define IWM_RX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_TSFT) | \ (1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTSIGNAL) | \ (1 << IEEE80211_RADIOTAP_DBM_ANTNOISE)) struct iwm_tx_radiotap_header { struct ieee80211_radiotap_header wt_ihdr; uint8_t wt_flags; uint8_t wt_rate; uint16_t wt_chan_freq; uint16_t wt_chan_flags; } __packed; #define IWM_TX_RADIOTAP_PRESENT \ ((1 << IEEE80211_RADIOTAP_FLAGS) | \ (1 << IEEE80211_RADIOTAP_RATE) | \ (1 << IEEE80211_RADIOTAP_CHANNEL)) -#define IWM_UCODE_SECT_MAX 6 +#define IWM_UCODE_SECT_MAX 16 #define IWM_FWDMASEGSZ (192*1024) +#define IWM_FWDMASEGSZ_8000 (320*1024) /* sanity check value */ #define IWM_FWMAXSIZE (2*1024*1024) /* * fw_status is used to determine if we've already parsed the firmware file * * In addition to the following, status < 0 ==> -error */ #define IWM_FW_STATUS_NONE 0 #define IWM_FW_STATUS_INPROGRESS 1 #define IWM_FW_STATUS_DONE 2 enum iwm_ucode_type { - IWM_UCODE_TYPE_INIT, IWM_UCODE_TYPE_REGULAR, + IWM_UCODE_TYPE_INIT, IWM_UCODE_TYPE_WOW, + IWM_UCODE_TYPE_REGULAR_USNIFFER, IWM_UCODE_TYPE_MAX }; struct iwm_fw_info { const struct firmware *fw_fp; int fw_status; struct iwm_fw_sects { struct iwm_fw_onesect { const void *fws_data; uint32_t fws_len; uint32_t fws_devoff; } fw_sect[IWM_UCODE_SECT_MAX]; size_t fw_totlen; int fw_count; } fw_sects[IWM_UCODE_TYPE_MAX]; }; struct iwm_nvm_data { int n_hw_addrs; uint8_t hw_addr[IEEE80211_ADDR_LEN]; uint8_t calib_version; uint16_t calib_voltage; uint16_t raw_temperature; uint16_t kelvin_temperature; uint16_t kelvin_voltage; uint16_t xtal_calib[2]; int sku_cap_band_24GHz_enable; int sku_cap_band_52GHz_enable; int sku_cap_11n_enable; int sku_cap_amt_enable; int sku_cap_ipan_enable; uint8_t radio_cfg_type; uint8_t radio_cfg_step; uint8_t radio_cfg_dash; uint8_t radio_cfg_pnum; uint8_t valid_tx_ant, valid_rx_ant; #define IWM_NUM_CHANNELS 39 +#define IWM_NUM_CHANNELS_8000 51 - uint16_t nvm_ch_flags[IWM_NUM_CHANNELS]; + uint16_t nvm_ch_flags[IWM_NUM_CHANNELS_8000]; uint16_t nvm_version; uint8_t max_tx_pwr_half_dbm; }; /* max bufs per tfd the driver will use */ #define IWM_MAX_CMD_TBS_PER_TFD 2 struct iwm_rx_packet; struct iwm_host_cmd { const void *data[IWM_MAX_CMD_TBS_PER_TFD]; struct iwm_rx_packet *resp_pkt; unsigned long _rx_page_addr; uint32_t _rx_page_order; int handler_status; uint32_t flags; + uint32_t id; uint16_t len[IWM_MAX_CMD_TBS_PER_TFD]; uint8_t dataflags[IWM_MAX_CMD_TBS_PER_TFD]; - uint8_t id; }; /* * DMA glue is from iwn */ typedef caddr_t iwm_caddr_t; typedef void *iwm_hookarg_t; struct iwm_dma_info { bus_dma_tag_t tag; bus_dmamap_t map; bus_dma_segment_t seg; bus_addr_t paddr; void *vaddr; bus_size_t size; }; #define IWM_TX_RING_COUNT 256 #define IWM_TX_RING_LOMARK 192 #define IWM_TX_RING_HIMARK 224 struct iwm_tx_data { bus_dmamap_t map; bus_addr_t cmd_paddr; bus_addr_t scratch_paddr; struct mbuf *m; struct iwm_node *in; int done; }; struct iwm_tx_ring { struct iwm_dma_info desc_dma; struct iwm_dma_info cmd_dma; struct iwm_tfd *desc; struct iwm_device_cmd *cmd; bus_dma_tag_t data_dmat; struct iwm_tx_data data[IWM_TX_RING_COUNT]; int qid; int queued; int cur; }; #define IWM_RX_RING_COUNT 256 #define IWM_RBUF_COUNT (IWM_RX_RING_COUNT + 32) /* Linux driver optionally uses 8k buffer */ #define IWM_RBUF_SIZE 4096 #define IWM_MAX_SCATTER 20 -struct iwm_softc; -struct iwm_rbuf { - struct iwm_softc *sc; - void *vaddr; - bus_addr_t paddr; -}; - struct iwm_rx_data { struct mbuf *m; bus_dmamap_t map; int wantresp; }; struct iwm_rx_ring { struct iwm_dma_info desc_dma; struct iwm_dma_info stat_dma; struct iwm_dma_info buf_dma; uint32_t *desc; struct iwm_rb_status *stat; struct iwm_rx_data data[IWM_RX_RING_COUNT]; bus_dmamap_t spare_map; /* for iwm_rx_addbuf() */ bus_dma_tag_t data_dmat; int cur; }; struct iwm_ucode_status { uint32_t uc_error_event_table; + uint32_t uc_umac_error_event_table; uint32_t uc_log_event_table; int uc_ok; int uc_intr; }; #define IWM_CMD_RESP_MAX PAGE_SIZE -#define IWM_OTP_LOW_IMAGE_SIZE 2048 +/* lower blocks contain EEPROM image and calibration data */ +#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000 16384 +#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000 32768 #define IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500 #define IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 /* * Command headers are in iwl-trans.h, which is full of all * kinds of other junk, so we just replicate the structures here. * First the software bits: */ enum IWM_CMD_MODE { IWM_CMD_SYNC = 0, IWM_CMD_ASYNC = (1 << 0), IWM_CMD_WANT_SKB = (1 << 1), IWM_CMD_SEND_IN_RFKILL = (1 << 2), }; enum iwm_hcmd_dataflag { IWM_HCMD_DFL_NOCOPY = (1 << 0), IWM_HCMD_DFL_DUP = (1 << 1), }; /* * iwlwifi/iwl-phy-db */ -#define IWM_NUM_PAPD_CH_GROUPS 4 +#define IWM_NUM_PAPD_CH_GROUPS 9 #define IWM_NUM_TXP_CH_GROUPS 9 struct iwm_phy_db_entry { uint16_t size; uint8_t *data; }; struct iwm_phy_db { struct iwm_phy_db_entry cfg; struct iwm_phy_db_entry calib_nch; struct iwm_phy_db_entry calib_ch_group_papd[IWM_NUM_PAPD_CH_GROUPS]; struct iwm_phy_db_entry calib_ch_group_txp[IWM_NUM_TXP_CH_GROUPS]; }; struct iwm_int_sta { uint32_t sta_id; uint32_t tfd_queue_msk; }; struct iwm_mvm_phy_ctxt { uint16_t id; uint16_t color; uint32_t ref; struct ieee80211_channel *channel; }; struct iwm_bf_data { int bf_enabled; /* filtering */ int ba_enabled; /* abort */ int ave_beacon_signal; int last_cqm_event; }; struct iwm_vap { struct ieee80211vap iv_vap; int is_uploaded; int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); }; #define IWM_VAP(_vap) ((struct iwm_vap *)(_vap)) struct iwm_node { struct ieee80211_node in_ni; struct iwm_mvm_phy_ctxt *in_phyctxt; /* status "bits" */ int in_assoc; struct iwm_lq_cmd in_lq; uint8_t in_ridx[IEEE80211_RATE_MAXSIZE]; }; #define IWM_NODE(_ni) ((struct iwm_node *)(_ni)) #define IWM_STATION_ID 0 +#define IWM_AUX_STA_ID 1 #define IWM_DEFAULT_MACID 0 #define IWM_DEFAULT_COLOR 0 #define IWM_DEFAULT_TSFID 0 #define IWM_ICT_SIZE 4096 #define IWM_ICT_COUNT (IWM_ICT_SIZE / sizeof (uint32_t)) #define IWM_ICT_PADDR_SHIFT 12 struct iwm_softc { device_t sc_dev; uint32_t sc_debug; struct mtx sc_mtx; struct mbufq sc_snd; struct ieee80211com sc_ic; int sc_flags; #define IWM_FLAG_USE_ICT (1 << 0) #define IWM_FLAG_HW_INITED (1 << 1) #define IWM_FLAG_STOPPED (1 << 2) #define IWM_FLAG_RFKILL (1 << 3) #define IWM_FLAG_BUSY (1 << 4) -#define IWM_FLAG_DORESUME (1 << 5) +#define IWM_FLAG_SCANNING (1 << 5) struct intr_config_hook sc_preinit_hook; struct callout sc_watchdog_to; struct callout sc_led_blink_to; struct task init_task; struct resource *sc_irq; struct resource *sc_mem; bus_space_tag_t sc_st; bus_space_handle_t sc_sh; bus_size_t sc_sz; bus_dma_tag_t sc_dmat; void *sc_ih; /* TX scheduler rings. */ struct iwm_dma_info sched_dma; uint32_t sched_base; /* TX/RX rings. */ struct iwm_tx_ring txq[IWM_MVM_MAX_QUEUES]; struct iwm_rx_ring rxq; int qfullmsk; int sc_sf_state; /* ICT table. */ struct iwm_dma_info ict_dma; int ict_cur; int sc_hw_rev; +#define IWM_SILICON_A_STEP 0 +#define IWM_SILICON_B_STEP 1 +#define IWM_SILICON_C_STEP 2 +#define IWM_SILICON_D_STEP 3 int sc_hw_id; + int sc_device_family; +#define IWM_DEVICE_FAMILY_7000 1 +#define IWM_DEVICE_FAMILY_8000 2 struct iwm_dma_info kw_dma; struct iwm_dma_info fw_dma; int sc_fw_chunk_done; int sc_init_complete; struct iwm_ucode_status sc_uc; enum iwm_ucode_type sc_uc_current; - int sc_fwver; + char sc_fwver[32]; int sc_capaflags; int sc_capa_max_probe_len; + int sc_capa_n_scan_channels; + uint32_t sc_ucode_api; + uint8_t sc_enabled_capa[howmany(IWM_NUM_UCODE_TLV_CAPA, NBBY)]; + char sc_fw_mcc[3]; int sc_intmask; /* * So why do we need a separate stopped flag and a generation? * the former protects the device from issuing commands when it's * stopped (duh). The latter protects against race from a very * fast stop/unstop cycle where threads waiting for responses do * not have a chance to run in between. Notably: we want to stop * the device from interrupt context when it craps out, so we * don't have the luxury of waiting for quiescense. */ int sc_generation; const char *sc_fwname; bus_size_t sc_fwdmasegsz; struct iwm_fw_info sc_fw; int sc_fw_phy_config; struct iwm_tlv_calib_ctrl sc_default_calib[IWM_UCODE_TYPE_MAX]; struct iwm_nvm_data sc_nvm; struct iwm_phy_db sc_phy_db; struct iwm_bf_data sc_bf; int sc_tx_timer; - struct iwm_scan_cmd *sc_scan_cmd; - size_t sc_scan_cmd_len; int sc_scan_last_antenna; - int sc_scanband; int sc_fixed_ridx; int sc_staid; int sc_nodecolor; uint8_t sc_cmd_resp[IWM_CMD_RESP_MAX]; int sc_wantresp; struct task sc_es_task; struct iwm_rx_phy_info sc_last_phy_info; int sc_ampdu_ref; struct iwm_int_sta sc_aux_sta; /* phy contexts. we only use the first one */ struct iwm_mvm_phy_ctxt sc_phyctxt[IWM_NUM_PHY_CTX]; struct iwm_notif_statistics sc_stats; int sc_noise; int host_interrupt_operation_mode; caddr_t sc_drvbpf; struct iwm_rx_radiotap_header sc_rxtap; struct iwm_tx_radiotap_header sc_txtap; int sc_max_rssi; }; #define IWM_LOCK_INIT(_sc) \ mtx_init(&(_sc)->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ MTX_NETWORK_LOCK, MTX_DEF); #define IWM_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define IWM_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define IWM_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx)