1330227Seadler/* $OpenBSD: if_iwm.c,v 1.167 2017/04/04 00:40:52 claudio Exp $ */ 2286441Srpaulo 3286441Srpaulo/* 4286441Srpaulo * Copyright (c) 2014 genua mbh <info@genua.de> 5286441Srpaulo * Copyright (c) 2014 Fixup Software Ltd. 6286441Srpaulo * 7286441Srpaulo * Permission to use, copy, modify, and distribute this software for any 8286441Srpaulo * purpose with or without fee is hereby granted, provided that the above 9286441Srpaulo * copyright notice and this permission notice appear in all copies. 10286441Srpaulo * 11286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18286441Srpaulo */ 19286441Srpaulo 20286441Srpaulo/*- 21286441Srpaulo * Based on BSD-licensed source modules in the Linux iwlwifi driver, 22286441Srpaulo * which were used as the reference documentation for this implementation. 23286441Srpaulo * 24286441Srpaulo * Driver version we are currently based off of is 25286441Srpaulo * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd) 26286441Srpaulo * 27286441Srpaulo *********************************************************************** 28286441Srpaulo * 29286441Srpaulo * This file is provided under a dual BSD/GPLv2 license. When using or 30286441Srpaulo * redistributing this file, you may do so under either license. 31286441Srpaulo * 32286441Srpaulo * GPL LICENSE SUMMARY 33286441Srpaulo * 34286441Srpaulo * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. 35286441Srpaulo * 36286441Srpaulo * This program is free software; you can redistribute it and/or modify 37286441Srpaulo * it under the terms of version 2 of the GNU General Public License as 38286441Srpaulo * published by the Free Software Foundation. 39286441Srpaulo * 40286441Srpaulo * This program is distributed in the hope that it will be useful, but 41286441Srpaulo * WITHOUT ANY WARRANTY; without even the implied warranty of 42286441Srpaulo * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 43286441Srpaulo * General Public License for more details. 44286441Srpaulo * 45286441Srpaulo * You should have received a copy of the GNU General Public License 46286441Srpaulo * along with this program; if not, write to the Free Software 47286441Srpaulo * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, 48286441Srpaulo * USA 49286441Srpaulo * 50286441Srpaulo * The full GNU General Public License is included in this distribution 51286441Srpaulo * in the file called COPYING. 52286441Srpaulo * 53286441Srpaulo * Contact Information: 54286441Srpaulo * Intel Linux Wireless <ilw@linux.intel.com> 55286441Srpaulo * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 56286441Srpaulo * 57286441Srpaulo * 58286441Srpaulo * BSD LICENSE 59286441Srpaulo * 60286441Srpaulo * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. 61286441Srpaulo * All rights reserved. 62286441Srpaulo * 63286441Srpaulo * Redistribution and use in source and binary forms, with or without 64286441Srpaulo * modification, are permitted provided that the following conditions 65286441Srpaulo * are met: 66286441Srpaulo * 67286441Srpaulo * * Redistributions of source code must retain the above copyright 68286441Srpaulo * notice, this list of conditions and the following disclaimer. 69286441Srpaulo * * Redistributions in binary form must reproduce the above copyright 70286441Srpaulo * notice, this list of conditions and the following disclaimer in 71286441Srpaulo * the documentation and/or other materials provided with the 72286441Srpaulo * distribution. 73286441Srpaulo * * Neither the name Intel Corporation nor the names of its 74286441Srpaulo * contributors may be used to endorse or promote products derived 75286441Srpaulo * from this software without specific prior written permission. 76286441Srpaulo * 77286441Srpaulo * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 78286441Srpaulo * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 79286441Srpaulo * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 80286441Srpaulo * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 81286441Srpaulo * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 82286441Srpaulo * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 83286441Srpaulo * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 84286441Srpaulo * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 85286441Srpaulo * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 86286441Srpaulo * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 87286441Srpaulo * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 88286441Srpaulo */ 89286441Srpaulo 90286441Srpaulo/*- 91286441Srpaulo * Copyright (c) 2007-2010 Damien Bergamini <damien.bergamini@free.fr> 92286441Srpaulo * 93286441Srpaulo * Permission to use, copy, modify, and distribute this software for any 94286441Srpaulo * purpose with or without fee is hereby granted, provided that the above 95286441Srpaulo * copyright notice and this permission notice appear in all copies. 96286441Srpaulo * 97286441Srpaulo * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 98286441Srpaulo * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 99286441Srpaulo * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 100286441Srpaulo * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 101286441Srpaulo * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 102286441Srpaulo * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 103286441Srpaulo * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 104286441Srpaulo */ 105286441Srpaulo#include <sys/cdefs.h> 106286441Srpaulo__FBSDID("$FreeBSD: stable/11/sys/dev/iwm/if_iwm.c 346035 2019-04-08 17:55:04Z kevans $"); 107286441Srpaulo 108300248Savos#include "opt_wlan.h" 109330455Seadler#include "opt_iwm.h" 110300248Savos 111286441Srpaulo#include <sys/param.h> 112286441Srpaulo#include <sys/bus.h> 113286441Srpaulo#include <sys/conf.h> 114286441Srpaulo#include <sys/endian.h> 115286441Srpaulo#include <sys/firmware.h> 116286441Srpaulo#include <sys/kernel.h> 117286441Srpaulo#include <sys/malloc.h> 118286441Srpaulo#include <sys/mbuf.h> 119286441Srpaulo#include <sys/mutex.h> 120286441Srpaulo#include <sys/module.h> 121286441Srpaulo#include <sys/proc.h> 122286441Srpaulo#include <sys/rman.h> 123286441Srpaulo#include <sys/socket.h> 124286441Srpaulo#include <sys/sockio.h> 125286441Srpaulo#include <sys/sysctl.h> 126286441Srpaulo#include <sys/linker.h> 127286441Srpaulo 128286441Srpaulo#include <machine/bus.h> 129286441Srpaulo#include <machine/endian.h> 130286441Srpaulo#include <machine/resource.h> 131286441Srpaulo 132286441Srpaulo#include <dev/pci/pcivar.h> 133286441Srpaulo#include <dev/pci/pcireg.h> 134286441Srpaulo 135286441Srpaulo#include <net/bpf.h> 136286441Srpaulo 137286441Srpaulo#include <net/if.h> 138286441Srpaulo#include <net/if_var.h> 139286441Srpaulo#include <net/if_arp.h> 140286441Srpaulo#include <net/if_dl.h> 141286441Srpaulo#include <net/if_media.h> 142286441Srpaulo#include <net/if_types.h> 143286441Srpaulo 144286441Srpaulo#include <netinet/in.h> 145286441Srpaulo#include <netinet/in_systm.h> 146286441Srpaulo#include <netinet/if_ether.h> 147286441Srpaulo#include <netinet/ip.h> 148286441Srpaulo 149286441Srpaulo#include <net80211/ieee80211_var.h> 150286441Srpaulo#include <net80211/ieee80211_regdomain.h> 151286441Srpaulo#include <net80211/ieee80211_ratectl.h> 152286441Srpaulo#include <net80211/ieee80211_radiotap.h> 153286441Srpaulo 154286475Srpaulo#include <dev/iwm/if_iwmreg.h> 155286475Srpaulo#include <dev/iwm/if_iwmvar.h> 156330188Seadler#include <dev/iwm/if_iwm_config.h> 157286475Srpaulo#include <dev/iwm/if_iwm_debug.h> 158330170Seadler#include <dev/iwm/if_iwm_notif_wait.h> 159286475Srpaulo#include <dev/iwm/if_iwm_util.h> 160286475Srpaulo#include <dev/iwm/if_iwm_binding.h> 161286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h> 162286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h> 163286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h> 164286475Srpaulo#include <dev/iwm/if_iwm_time_event.h> 165286475Srpaulo#include <dev/iwm/if_iwm_power.h> 166286475Srpaulo#include <dev/iwm/if_iwm_scan.h> 167330210Seadler#include <dev/iwm/if_iwm_sta.h> 168286441Srpaulo 169286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h> 170301187Sadrian#include <dev/iwm/if_iwm_led.h> 171330192Seadler#include <dev/iwm/if_iwm_fw.h> 172286441Srpaulo 173330208Seadler/* From DragonflyBSD */ 174330208Seadler#define mtodoff(m, t, off) ((t)((m)->m_data + (off))) 175330208Seadler 176286441Srpauloconst uint8_t iwm_nvm_channels[] = { 177286441Srpaulo /* 2.4 GHz */ 178286441Srpaulo 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 179286441Srpaulo /* 5 GHz */ 180298877Savos 36, 40, 44, 48, 52, 56, 60, 64, 181286441Srpaulo 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 182286441Srpaulo 149, 153, 157, 161, 165 183286441Srpaulo}; 184298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS, 185298877Savos "IWM_NUM_CHANNELS is too small"); 186298877Savos 187303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = { 188303628Ssbruno /* 2.4 GHz */ 189303628Ssbruno 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 190303628Ssbruno /* 5 GHz */ 191303628Ssbruno 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 192303628Ssbruno 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 193303628Ssbruno 149, 153, 157, 161, 165, 169, 173, 177, 181 194303628Ssbruno}; 195303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000, 196303628Ssbruno "IWM_NUM_CHANNELS_8000 is too small"); 197303628Ssbruno 198303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS 14 199303628Ssbruno#define IWM_N_HW_ADDR_MASK 0xF 200303628Ssbruno 201286441Srpaulo/* 202286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries 203286441Srpaulo * that are populated. 204286441Srpaulo */ 205286441Srpauloconst struct iwm_rate { 206286441Srpaulo uint8_t rate; 207286441Srpaulo uint8_t plcp; 208286441Srpaulo} iwm_rates[] = { 209286441Srpaulo { 2, IWM_RATE_1M_PLCP }, 210286441Srpaulo { 4, IWM_RATE_2M_PLCP }, 211286441Srpaulo { 11, IWM_RATE_5M_PLCP }, 212286441Srpaulo { 22, IWM_RATE_11M_PLCP }, 213286441Srpaulo { 12, IWM_RATE_6M_PLCP }, 214286441Srpaulo { 18, IWM_RATE_9M_PLCP }, 215286441Srpaulo { 24, IWM_RATE_12M_PLCP }, 216286441Srpaulo { 36, IWM_RATE_18M_PLCP }, 217286441Srpaulo { 48, IWM_RATE_24M_PLCP }, 218286441Srpaulo { 72, IWM_RATE_36M_PLCP }, 219286441Srpaulo { 96, IWM_RATE_48M_PLCP }, 220286441Srpaulo { 108, IWM_RATE_54M_PLCP }, 221286441Srpaulo}; 222286441Srpaulo#define IWM_RIDX_CCK 0 223286441Srpaulo#define IWM_RIDX_OFDM 4 224286441Srpaulo#define IWM_RIDX_MAX (nitems(iwm_rates)-1) 225286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) 226286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) 227286441Srpaulo 228303628Ssbrunostruct iwm_nvm_section { 229303628Ssbruno uint16_t length; 230303628Ssbruno uint8_t *data; 231303628Ssbruno}; 232303628Ssbruno 233330183Seadler#define IWM_MVM_UCODE_ALIVE_TIMEOUT hz 234330171Seadler#define IWM_MVM_UCODE_CALIB_TIMEOUT (2*hz) 235330171Seadler 236330183Seadlerstruct iwm_mvm_alive_data { 237330183Seadler int valid; 238330183Seadler uint32_t scd_base_addr; 239330183Seadler}; 240330183Seadler 241286441Srpaulostatic int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); 242286441Srpaulostatic int iwm_firmware_store_section(struct iwm_softc *, 243286441Srpaulo enum iwm_ucode_type, 244286441Srpaulo const uint8_t *, size_t); 245286441Srpaulostatic int iwm_set_default_calib(struct iwm_softc *, const void *); 246286441Srpaulostatic void iwm_fw_info_free(struct iwm_fw_info *); 247286441Srpaulostatic int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); 248286441Srpaulostatic int iwm_alloc_fwmem(struct iwm_softc *); 249286441Srpaulostatic int iwm_alloc_sched(struct iwm_softc *); 250286441Srpaulostatic int iwm_alloc_kw(struct iwm_softc *); 251286441Srpaulostatic int iwm_alloc_ict(struct iwm_softc *); 252286441Srpaulostatic int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 253286441Srpaulostatic void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 254286441Srpaulostatic void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 255286441Srpaulostatic int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, 256286441Srpaulo int); 257286441Srpaulostatic void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 258286441Srpaulostatic void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 259286441Srpaulostatic void iwm_enable_interrupts(struct iwm_softc *); 260286441Srpaulostatic void iwm_restore_interrupts(struct iwm_softc *); 261286441Srpaulostatic void iwm_disable_interrupts(struct iwm_softc *); 262286441Srpaulostatic void iwm_ict_reset(struct iwm_softc *); 263286441Srpaulostatic int iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *); 264286441Srpaulostatic void iwm_stop_device(struct iwm_softc *); 265286441Srpaulostatic void iwm_mvm_nic_config(struct iwm_softc *); 266286441Srpaulostatic int iwm_nic_rx_init(struct iwm_softc *); 267286441Srpaulostatic int iwm_nic_tx_init(struct iwm_softc *); 268286441Srpaulostatic int iwm_nic_init(struct iwm_softc *); 269330183Seadlerstatic int iwm_trans_pcie_fw_alive(struct iwm_softc *, uint32_t); 270286441Srpaulostatic int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, 271286441Srpaulo uint16_t, uint8_t *, uint16_t *); 272286441Srpaulostatic int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, 273330165Seadler uint16_t *, uint32_t); 274298877Savosstatic uint32_t iwm_eeprom_channel_flags(uint16_t); 275298877Savosstatic void iwm_add_channel_band(struct iwm_softc *, 276303628Ssbruno struct ieee80211_channel[], int, int *, int, size_t, 277298877Savos const uint8_t[]); 278298877Savosstatic void iwm_init_channel_map(struct ieee80211com *, int, int *, 279298877Savos struct ieee80211_channel[]); 280330165Seadlerstatic struct iwm_nvm_data * 281330165Seadler iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, 282330165Seadler const uint16_t *, const uint16_t *, 283330165Seadler const uint16_t *, const uint16_t *, 284330165Seadler const uint16_t *); 285330165Seadlerstatic void iwm_free_nvm_data(struct iwm_nvm_data *); 286330165Seadlerstatic void iwm_set_hw_address_family_8000(struct iwm_softc *, 287330165Seadler struct iwm_nvm_data *, 288330165Seadler const uint16_t *, 289330165Seadler const uint16_t *); 290303628Ssbrunostatic int iwm_get_sku(const struct iwm_softc *, const uint16_t *, 291303628Ssbruno const uint16_t *); 292303628Ssbrunostatic int iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *); 293303628Ssbrunostatic int iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *, 294303628Ssbruno const uint16_t *); 295303628Ssbrunostatic int iwm_get_n_hw_addrs(const struct iwm_softc *, 296303628Ssbruno const uint16_t *); 297303628Ssbrunostatic void iwm_set_radio_cfg(const struct iwm_softc *, 298303628Ssbruno struct iwm_nvm_data *, uint32_t); 299330165Seadlerstatic struct iwm_nvm_data * 300330165Seadler iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); 301286441Srpaulostatic int iwm_nvm_init(struct iwm_softc *); 302330182Seadlerstatic int iwm_pcie_load_section(struct iwm_softc *, uint8_t, 303330182Seadler const struct iwm_fw_desc *); 304330182Seadlerstatic int iwm_pcie_load_firmware_chunk(struct iwm_softc *, uint32_t, 305330182Seadler bus_addr_t, uint32_t); 306330182Seadlerstatic int iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, 307330182Seadler const struct iwm_fw_sects *, 308330182Seadler int, int *); 309330182Seadlerstatic int iwm_pcie_load_cpu_sections(struct iwm_softc *, 310330182Seadler const struct iwm_fw_sects *, 311330182Seadler int, int *); 312330182Seadlerstatic int iwm_pcie_load_given_ucode_8000(struct iwm_softc *, 313330182Seadler const struct iwm_fw_sects *); 314330182Seadlerstatic int iwm_pcie_load_given_ucode(struct iwm_softc *, 315330182Seadler const struct iwm_fw_sects *); 316330183Seadlerstatic int iwm_start_fw(struct iwm_softc *, const struct iwm_fw_sects *); 317286441Srpaulostatic int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); 318286441Srpaulostatic int iwm_send_phy_cfg_cmd(struct iwm_softc *); 319286441Srpaulostatic int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, 320286441Srpaulo enum iwm_ucode_type); 321286441Srpaulostatic int iwm_run_init_mvm_ucode(struct iwm_softc *, int); 322286441Srpaulostatic int iwm_rx_addbuf(struct iwm_softc *, int, int); 323286441Srpaulostatic int iwm_mvm_get_signal_strength(struct iwm_softc *, 324286441Srpaulo struct iwm_rx_phy_info *); 325286441Srpaulostatic void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, 326330208Seadler struct iwm_rx_packet *); 327330224Seadlerstatic int iwm_get_noise(struct iwm_softc *, 328330144Seadler const struct iwm_mvm_statistics_rx_non_phy *); 329330224Seadlerstatic void iwm_mvm_handle_rx_statistics(struct iwm_softc *, 330330224Seadler struct iwm_rx_packet *); 331330208Seadlerstatic boolean_t iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct mbuf *, 332330208Seadler uint32_t, boolean_t); 333293100Savosstatic int iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, 334286441Srpaulo struct iwm_rx_packet *, 335286441Srpaulo struct iwm_node *); 336330208Seadlerstatic void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *); 337286441Srpaulostatic void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); 338286441Srpaulo#if 0 339286441Srpaulostatic void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, 340286441Srpaulo uint16_t); 341286441Srpaulo#endif 342286441Srpaulostatic const struct iwm_rate * 343286441Srpaulo iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, 344330155Seadler struct mbuf *, struct iwm_tx_cmd *); 345286441Srpaulostatic int iwm_tx(struct iwm_softc *, struct mbuf *, 346286441Srpaulo struct ieee80211_node *, int); 347286441Srpaulostatic int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, 348286441Srpaulo const struct ieee80211_bpf_params *); 349330203Seadlerstatic int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_vap *); 350286441Srpaulostatic int iwm_auth(struct ieee80211vap *, struct iwm_softc *); 351286441Srpaulostatic int iwm_assoc(struct ieee80211vap *, struct iwm_softc *); 352286441Srpaulostatic int iwm_release(struct iwm_softc *, struct iwm_node *); 353286441Srpaulostatic struct ieee80211_node * 354286441Srpaulo iwm_node_alloc(struct ieee80211vap *, 355286441Srpaulo const uint8_t[IEEE80211_ADDR_LEN]); 356286441Srpaulostatic void iwm_setrates(struct iwm_softc *, struct iwm_node *); 357286441Srpaulostatic int iwm_media_change(struct ifnet *); 358286441Srpaulostatic int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); 359286441Srpaulostatic void iwm_endscan_cb(void *, int); 360303628Ssbrunostatic void iwm_mvm_fill_sf_command(struct iwm_softc *, 361303628Ssbruno struct iwm_sf_cfg_cmd *, 362303628Ssbruno struct ieee80211_node *); 363303628Ssbrunostatic int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); 364303628Ssbrunostatic int iwm_send_bt_init_conf(struct iwm_softc *); 365330217Seadlerstatic boolean_t iwm_mvm_is_lar_supported(struct iwm_softc *); 366330217Seadlerstatic boolean_t iwm_mvm_is_wifi_mcc_supported(struct iwm_softc *); 367303628Ssbrunostatic int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); 368303628Ssbrunostatic void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); 369286441Srpaulostatic int iwm_init_hw(struct iwm_softc *); 370287197Sglebiusstatic void iwm_init(struct iwm_softc *); 371287197Sglebiusstatic void iwm_start(struct iwm_softc *); 372287197Sglebiusstatic void iwm_stop(struct iwm_softc *); 373286441Srpaulostatic void iwm_watchdog(void *); 374287197Sglebiusstatic void iwm_parent(struct ieee80211com *); 375286441Srpaulo#ifdef IWM_DEBUG 376286441Srpaulostatic const char * 377286441Srpaulo iwm_desc_lookup(uint32_t); 378286441Srpaulostatic void iwm_nic_error(struct iwm_softc *); 379303628Ssbrunostatic void iwm_nic_umac_error(struct iwm_softc *); 380286441Srpaulo#endif 381330208Seadlerstatic void iwm_handle_rxb(struct iwm_softc *, struct mbuf *); 382286441Srpaulostatic void iwm_notif_intr(struct iwm_softc *); 383286441Srpaulostatic void iwm_intr(void *); 384286441Srpaulostatic int iwm_attach(device_t); 385303628Ssbrunostatic int iwm_is_valid_ether_addr(uint8_t *); 386286441Srpaulostatic void iwm_preinit(void *); 387286441Srpaulostatic int iwm_detach_local(struct iwm_softc *sc, int); 388286441Srpaulostatic void iwm_init_task(void *); 389286441Srpaulostatic void iwm_radiotap_attach(struct iwm_softc *); 390286441Srpaulostatic struct ieee80211vap * 391286441Srpaulo iwm_vap_create(struct ieee80211com *, 392286441Srpaulo const char [IFNAMSIZ], int, 393286441Srpaulo enum ieee80211_opmode, int, 394286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 395286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN]); 396286441Srpaulostatic void iwm_vap_delete(struct ieee80211vap *); 397330223Seadlerstatic void iwm_xmit_queue_drain(struct iwm_softc *); 398286441Srpaulostatic void iwm_scan_start(struct ieee80211com *); 399286441Srpaulostatic void iwm_scan_end(struct ieee80211com *); 400286441Srpaulostatic void iwm_update_mcast(struct ieee80211com *); 401286441Srpaulostatic void iwm_set_channel(struct ieee80211com *); 402286441Srpaulostatic void iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long); 403286441Srpaulostatic void iwm_scan_mindwell(struct ieee80211_scan_state *); 404286441Srpaulostatic int iwm_detach(device_t); 405286441Srpaulo 406330217Seadlerstatic int iwm_lar_disable = 0; 407330217SeadlerTUNABLE_INT("hw.iwm.lar.disable", &iwm_lar_disable); 408330217Seadler 409286441Srpaulo/* 410286441Srpaulo * Firmware parser. 411286441Srpaulo */ 412286441Srpaulo 413286441Srpaulostatic int 414286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen) 415286441Srpaulo{ 416286441Srpaulo const struct iwm_fw_cscheme_list *l = (const void *)data; 417286441Srpaulo 418286441Srpaulo if (dlen < sizeof(*l) || 419286441Srpaulo dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) 420286441Srpaulo return EINVAL; 421286441Srpaulo 422286441Srpaulo /* we don't actually store anything for now, always use s/w crypto */ 423286441Srpaulo 424286441Srpaulo return 0; 425286441Srpaulo} 426286441Srpaulo 427286441Srpaulostatic int 428286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc, 429286441Srpaulo enum iwm_ucode_type type, const uint8_t *data, size_t dlen) 430286441Srpaulo{ 431286441Srpaulo struct iwm_fw_sects *fws; 432330182Seadler struct iwm_fw_desc *fwone; 433286441Srpaulo 434286441Srpaulo if (type >= IWM_UCODE_TYPE_MAX) 435286441Srpaulo return EINVAL; 436286441Srpaulo if (dlen < sizeof(uint32_t)) 437286441Srpaulo return EINVAL; 438286441Srpaulo 439286441Srpaulo fws = &sc->sc_fw.fw_sects[type]; 440330168Seadler if (fws->fw_count >= IWM_UCODE_SECTION_MAX) 441286441Srpaulo return EINVAL; 442286441Srpaulo 443286441Srpaulo fwone = &fws->fw_sect[fws->fw_count]; 444286441Srpaulo 445286441Srpaulo /* first 32bit are device load offset */ 446330182Seadler memcpy(&fwone->offset, data, sizeof(uint32_t)); 447286441Srpaulo 448286441Srpaulo /* rest is data */ 449330182Seadler fwone->data = data + sizeof(uint32_t); 450330182Seadler fwone->len = dlen - sizeof(uint32_t); 451286441Srpaulo 452286441Srpaulo fws->fw_count++; 453286441Srpaulo 454286441Srpaulo return 0; 455286441Srpaulo} 456286441Srpaulo 457330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40 458330152Seadler 459286441Srpaulo/* iwlwifi: iwl-drv.c */ 460286441Srpaulostruct iwm_tlv_calib_data { 461286441Srpaulo uint32_t ucode_type; 462286441Srpaulo struct iwm_tlv_calib_ctrl calib; 463286441Srpaulo} __packed; 464286441Srpaulo 465286441Srpaulostatic int 466286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data) 467286441Srpaulo{ 468286441Srpaulo const struct iwm_tlv_calib_data *def_calib = data; 469286441Srpaulo uint32_t ucode_type = le32toh(def_calib->ucode_type); 470286441Srpaulo 471286441Srpaulo if (ucode_type >= IWM_UCODE_TYPE_MAX) { 472286441Srpaulo device_printf(sc->sc_dev, 473286441Srpaulo "Wrong ucode_type %u for default " 474286441Srpaulo "calibration.\n", ucode_type); 475286441Srpaulo return EINVAL; 476286441Srpaulo } 477286441Srpaulo 478286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger = 479286441Srpaulo def_calib->calib.flow_trigger; 480286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger = 481286441Srpaulo def_calib->calib.event_trigger; 482286441Srpaulo 483286441Srpaulo return 0; 484286441Srpaulo} 485286441Srpaulo 486330196Seadlerstatic int 487330196Seadleriwm_set_ucode_api_flags(struct iwm_softc *sc, const uint8_t *data, 488330196Seadler struct iwm_ucode_capabilities *capa) 489330196Seadler{ 490330196Seadler const struct iwm_ucode_api *ucode_api = (const void *)data; 491330196Seadler uint32_t api_index = le32toh(ucode_api->api_index); 492330196Seadler uint32_t api_flags = le32toh(ucode_api->api_flags); 493330196Seadler int i; 494330196Seadler 495330196Seadler if (api_index >= howmany(IWM_NUM_UCODE_TLV_API, 32)) { 496330196Seadler device_printf(sc->sc_dev, 497330196Seadler "api flags index %d larger than supported by driver\n", 498330196Seadler api_index); 499330196Seadler /* don't return an error so we can load FW that has more bits */ 500330196Seadler return 0; 501330196Seadler } 502330196Seadler 503330196Seadler for (i = 0; i < 32; i++) { 504330196Seadler if (api_flags & (1U << i)) 505330196Seadler setbit(capa->enabled_api, i + 32 * api_index); 506330196Seadler } 507330196Seadler 508330196Seadler return 0; 509330196Seadler} 510330196Seadler 511330196Seadlerstatic int 512330196Seadleriwm_set_ucode_capabilities(struct iwm_softc *sc, const uint8_t *data, 513330196Seadler struct iwm_ucode_capabilities *capa) 514330196Seadler{ 515330196Seadler const struct iwm_ucode_capa *ucode_capa = (const void *)data; 516330196Seadler uint32_t api_index = le32toh(ucode_capa->api_index); 517330196Seadler uint32_t api_flags = le32toh(ucode_capa->api_capa); 518330196Seadler int i; 519330196Seadler 520330196Seadler if (api_index >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { 521330196Seadler device_printf(sc->sc_dev, 522330196Seadler "capa flags index %d larger than supported by driver\n", 523330196Seadler api_index); 524330196Seadler /* don't return an error so we can load FW that has more bits */ 525330196Seadler return 0; 526330196Seadler } 527330196Seadler 528330196Seadler for (i = 0; i < 32; i++) { 529330196Seadler if (api_flags & (1U << i)) 530330196Seadler setbit(capa->enabled_capa, i + 32 * api_index); 531330196Seadler } 532330196Seadler 533330196Seadler return 0; 534330196Seadler} 535330196Seadler 536286441Srpaulostatic void 537286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw) 538286441Srpaulo{ 539293177Savos firmware_put(fw->fw_fp, FIRMWARE_UNLOAD); 540293177Savos fw->fw_fp = NULL; 541286441Srpaulo /* don't touch fw->fw_status */ 542286441Srpaulo memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); 543286441Srpaulo} 544286441Srpaulo 545286441Srpaulostatic int 546286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 547286441Srpaulo{ 548286441Srpaulo struct iwm_fw_info *fw = &sc->sc_fw; 549286441Srpaulo const struct iwm_tlv_ucode_header *uhdr; 550330784Seadler struct iwm_ucode_tlv tlv; 551330196Seadler struct iwm_ucode_capabilities *capa = &sc->ucode_capa; 552286441Srpaulo enum iwm_ucode_tlv_type tlv_type; 553286441Srpaulo const struct firmware *fwp; 554286441Srpaulo const uint8_t *data; 555330168Seadler uint32_t usniffer_img; 556330168Seadler uint32_t paging_mem_size; 557330182Seadler int num_of_cpus; 558286441Srpaulo int error = 0; 559286441Srpaulo size_t len; 560286441Srpaulo 561286441Srpaulo if (fw->fw_status == IWM_FW_STATUS_DONE && 562330168Seadler ucode_type != IWM_UCODE_INIT) 563286441Srpaulo return 0; 564286441Srpaulo 565286441Srpaulo while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) 566286441Srpaulo msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); 567286441Srpaulo fw->fw_status = IWM_FW_STATUS_INPROGRESS; 568286441Srpaulo 569293177Savos if (fw->fw_fp != NULL) 570286441Srpaulo iwm_fw_info_free(fw); 571286441Srpaulo 572286441Srpaulo /* 573286441Srpaulo * Load firmware into driver memory. 574293177Savos * fw_fp will be set. 575286441Srpaulo */ 576286441Srpaulo IWM_UNLOCK(sc); 577330166Seadler fwp = firmware_get(sc->cfg->fw_name); 578293177Savos IWM_LOCK(sc); 579286441Srpaulo if (fwp == NULL) { 580286441Srpaulo device_printf(sc->sc_dev, 581286441Srpaulo "could not read firmware %s (error %d)\n", 582330166Seadler sc->cfg->fw_name, error); 583286441Srpaulo goto out; 584286441Srpaulo } 585293177Savos fw->fw_fp = fwp; 586286441Srpaulo 587303628Ssbruno /* (Re-)Initialize default values. */ 588330196Seadler capa->flags = 0; 589330196Seadler capa->max_probe_length = IWM_DEFAULT_MAX_PROBE_LENGTH; 590330196Seadler capa->n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS; 591330196Seadler memset(capa->enabled_capa, 0, sizeof(capa->enabled_capa)); 592330196Seadler memset(capa->enabled_api, 0, sizeof(capa->enabled_api)); 593303628Ssbruno memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); 594303628Ssbruno 595286441Srpaulo /* 596286441Srpaulo * Parse firmware contents 597286441Srpaulo */ 598286441Srpaulo 599293177Savos uhdr = (const void *)fw->fw_fp->data; 600293177Savos if (*(const uint32_t *)fw->fw_fp->data != 0 601286441Srpaulo || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { 602286441Srpaulo device_printf(sc->sc_dev, "invalid firmware %s\n", 603330166Seadler sc->cfg->fw_name); 604286441Srpaulo error = EINVAL; 605286441Srpaulo goto out; 606286441Srpaulo } 607286441Srpaulo 608330784Seadler snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", 609303628Ssbruno IWM_UCODE_MAJOR(le32toh(uhdr->ver)), 610303628Ssbruno IWM_UCODE_MINOR(le32toh(uhdr->ver)), 611303628Ssbruno IWM_UCODE_API(le32toh(uhdr->ver))); 612286441Srpaulo data = uhdr->data; 613293177Savos len = fw->fw_fp->datasize - sizeof(*uhdr); 614286441Srpaulo 615330784Seadler while (len >= sizeof(tlv)) { 616330784Seadler size_t tlv_len; 617330784Seadler const void *tlv_data; 618286441Srpaulo 619330784Seadler memcpy(&tlv, data, sizeof(tlv)); 620330784Seadler tlv_len = le32toh(tlv.length); 621330784Seadler tlv_type = le32toh(tlv.type); 622286441Srpaulo 623330784Seadler len -= sizeof(tlv); 624330784Seadler data += sizeof(tlv); 625330784Seadler tlv_data = data; 626330784Seadler 627286441Srpaulo if (len < tlv_len) { 628286441Srpaulo device_printf(sc->sc_dev, 629286441Srpaulo "firmware too short: %zu bytes\n", 630286441Srpaulo len); 631286441Srpaulo error = EINVAL; 632286441Srpaulo goto parse_out; 633286441Srpaulo } 634286441Srpaulo 635286441Srpaulo switch ((int)tlv_type) { 636286441Srpaulo case IWM_UCODE_TLV_PROBE_MAX_LEN: 637330784Seadler if (tlv_len < sizeof(uint32_t)) { 638286441Srpaulo device_printf(sc->sc_dev, 639330784Seadler "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n", 640286441Srpaulo __func__, 641286441Srpaulo (int) tlv_len); 642286441Srpaulo error = EINVAL; 643286441Srpaulo goto parse_out; 644286441Srpaulo } 645330196Seadler capa->max_probe_length = 646330784Seadler le32toh(*(const uint32_t *)tlv_data); 647286441Srpaulo /* limit it to something sensible */ 648330196Seadler if (capa->max_probe_length > 649303628Ssbruno IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { 650286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 651286441Srpaulo "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " 652286441Srpaulo "ridiculous\n", __func__); 653286441Srpaulo error = EINVAL; 654286441Srpaulo goto parse_out; 655286441Srpaulo } 656286441Srpaulo break; 657286441Srpaulo case IWM_UCODE_TLV_PAN: 658286441Srpaulo if (tlv_len) { 659286441Srpaulo device_printf(sc->sc_dev, 660286441Srpaulo "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", 661286441Srpaulo __func__, 662286441Srpaulo (int) tlv_len); 663286441Srpaulo error = EINVAL; 664286441Srpaulo goto parse_out; 665286441Srpaulo } 666330196Seadler capa->flags |= IWM_UCODE_TLV_FLAGS_PAN; 667286441Srpaulo break; 668286441Srpaulo case IWM_UCODE_TLV_FLAGS: 669286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 670286441Srpaulo device_printf(sc->sc_dev, 671286441Srpaulo "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", 672286441Srpaulo __func__, 673286441Srpaulo (int) tlv_len); 674286441Srpaulo error = EINVAL; 675286441Srpaulo goto parse_out; 676286441Srpaulo } 677286441Srpaulo /* 678286441Srpaulo * Apparently there can be many flags, but Linux driver 679286441Srpaulo * parses only the first one, and so do we. 680286441Srpaulo * 681286441Srpaulo * XXX: why does this override IWM_UCODE_TLV_PAN? 682286441Srpaulo * Intentional or a bug? Observations from 683286441Srpaulo * current firmware file: 684286441Srpaulo * 1) TLV_PAN is parsed first 685286441Srpaulo * 2) TLV_FLAGS contains TLV_FLAGS_PAN 686286441Srpaulo * ==> this resets TLV_PAN to itself... hnnnk 687286441Srpaulo */ 688330784Seadler capa->flags = le32toh(*(const uint32_t *)tlv_data); 689286441Srpaulo break; 690286441Srpaulo case IWM_UCODE_TLV_CSCHEME: 691286441Srpaulo if ((error = iwm_store_cscheme(sc, 692286441Srpaulo tlv_data, tlv_len)) != 0) { 693286441Srpaulo device_printf(sc->sc_dev, 694286441Srpaulo "%s: iwm_store_cscheme(): returned %d\n", 695286441Srpaulo __func__, 696286441Srpaulo error); 697286441Srpaulo goto parse_out; 698286441Srpaulo } 699286441Srpaulo break; 700330182Seadler case IWM_UCODE_TLV_NUM_OF_CPU: 701286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 702286441Srpaulo device_printf(sc->sc_dev, 703330182Seadler "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) != sizeof(uint32_t)\n", 704286441Srpaulo __func__, 705286441Srpaulo (int) tlv_len); 706286441Srpaulo error = EINVAL; 707286441Srpaulo goto parse_out; 708286441Srpaulo } 709330784Seadler num_of_cpus = le32toh(*(const uint32_t *)tlv_data); 710330182Seadler if (num_of_cpus == 2) { 711330182Seadler fw->fw_sects[IWM_UCODE_REGULAR].is_dual_cpus = 712330182Seadler TRUE; 713330182Seadler fw->fw_sects[IWM_UCODE_INIT].is_dual_cpus = 714330182Seadler TRUE; 715330182Seadler fw->fw_sects[IWM_UCODE_WOWLAN].is_dual_cpus = 716330182Seadler TRUE; 717330182Seadler } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { 718286441Srpaulo device_printf(sc->sc_dev, 719303628Ssbruno "%s: Driver supports only 1 or 2 CPUs\n", 720286441Srpaulo __func__); 721286441Srpaulo error = EINVAL; 722286441Srpaulo goto parse_out; 723286441Srpaulo } 724286441Srpaulo break; 725286441Srpaulo case IWM_UCODE_TLV_SEC_RT: 726286441Srpaulo if ((error = iwm_firmware_store_section(sc, 727330168Seadler IWM_UCODE_REGULAR, tlv_data, tlv_len)) != 0) { 728286441Srpaulo device_printf(sc->sc_dev, 729330168Seadler "%s: IWM_UCODE_REGULAR: iwm_firmware_store_section() failed; %d\n", 730286441Srpaulo __func__, 731286441Srpaulo error); 732286441Srpaulo goto parse_out; 733286441Srpaulo } 734286441Srpaulo break; 735286441Srpaulo case IWM_UCODE_TLV_SEC_INIT: 736286441Srpaulo if ((error = iwm_firmware_store_section(sc, 737330168Seadler IWM_UCODE_INIT, tlv_data, tlv_len)) != 0) { 738286441Srpaulo device_printf(sc->sc_dev, 739330168Seadler "%s: IWM_UCODE_INIT: iwm_firmware_store_section() failed; %d\n", 740286441Srpaulo __func__, 741286441Srpaulo error); 742286441Srpaulo goto parse_out; 743286441Srpaulo } 744286441Srpaulo break; 745286441Srpaulo case IWM_UCODE_TLV_SEC_WOWLAN: 746286441Srpaulo if ((error = iwm_firmware_store_section(sc, 747330168Seadler IWM_UCODE_WOWLAN, tlv_data, tlv_len)) != 0) { 748286441Srpaulo device_printf(sc->sc_dev, 749330168Seadler "%s: IWM_UCODE_WOWLAN: iwm_firmware_store_section() failed; %d\n", 750286441Srpaulo __func__, 751286441Srpaulo error); 752286441Srpaulo goto parse_out; 753286441Srpaulo } 754286441Srpaulo break; 755286441Srpaulo case IWM_UCODE_TLV_DEF_CALIB: 756286441Srpaulo if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { 757286441Srpaulo device_printf(sc->sc_dev, 758286441Srpaulo "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", 759286441Srpaulo __func__, 760286441Srpaulo (int) tlv_len, 761286441Srpaulo (int) sizeof(struct iwm_tlv_calib_data)); 762286441Srpaulo error = EINVAL; 763286441Srpaulo goto parse_out; 764286441Srpaulo } 765286441Srpaulo if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { 766286441Srpaulo device_printf(sc->sc_dev, 767286441Srpaulo "%s: iwm_set_default_calib() failed: %d\n", 768286441Srpaulo __func__, 769286441Srpaulo error); 770286441Srpaulo goto parse_out; 771286441Srpaulo } 772286441Srpaulo break; 773286441Srpaulo case IWM_UCODE_TLV_PHY_SKU: 774286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 775286441Srpaulo error = EINVAL; 776286441Srpaulo device_printf(sc->sc_dev, 777286441Srpaulo "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", 778286441Srpaulo __func__, 779286441Srpaulo (int) tlv_len); 780286441Srpaulo goto parse_out; 781286441Srpaulo } 782330167Seadler sc->sc_fw.phy_config = 783330784Seadler le32toh(*(const uint32_t *)tlv_data); 784330167Seadler sc->sc_fw.valid_tx_ant = (sc->sc_fw.phy_config & 785330167Seadler IWM_FW_PHY_CFG_TX_CHAIN) >> 786330167Seadler IWM_FW_PHY_CFG_TX_CHAIN_POS; 787330167Seadler sc->sc_fw.valid_rx_ant = (sc->sc_fw.phy_config & 788330167Seadler IWM_FW_PHY_CFG_RX_CHAIN) >> 789330167Seadler IWM_FW_PHY_CFG_RX_CHAIN_POS; 790286441Srpaulo break; 791286441Srpaulo 792303628Ssbruno case IWM_UCODE_TLV_API_CHANGES_SET: { 793330196Seadler if (tlv_len != sizeof(struct iwm_ucode_api)) { 794303628Ssbruno error = EINVAL; 795303628Ssbruno goto parse_out; 796303628Ssbruno } 797330196Seadler if (iwm_set_ucode_api_flags(sc, tlv_data, capa)) { 798330196Seadler error = EINVAL; 799303628Ssbruno goto parse_out; 800303628Ssbruno } 801303628Ssbruno break; 802303628Ssbruno } 803303628Ssbruno 804303628Ssbruno case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { 805330196Seadler if (tlv_len != sizeof(struct iwm_ucode_capa)) { 806303628Ssbruno error = EINVAL; 807303628Ssbruno goto parse_out; 808303628Ssbruno } 809330196Seadler if (iwm_set_ucode_capabilities(sc, tlv_data, capa)) { 810330196Seadler error = EINVAL; 811303628Ssbruno goto parse_out; 812303628Ssbruno } 813303628Ssbruno break; 814303628Ssbruno } 815303628Ssbruno 816303628Ssbruno case 48: /* undocumented TLV */ 817303628Ssbruno case IWM_UCODE_TLV_SDIO_ADMA_ADDR: 818303628Ssbruno case IWM_UCODE_TLV_FW_GSCAN_CAPA: 819286441Srpaulo /* ignore, not used by current driver */ 820286441Srpaulo break; 821286441Srpaulo 822303628Ssbruno case IWM_UCODE_TLV_SEC_RT_USNIFFER: 823303628Ssbruno if ((error = iwm_firmware_store_section(sc, 824330168Seadler IWM_UCODE_REGULAR_USNIFFER, tlv_data, 825303628Ssbruno tlv_len)) != 0) 826303628Ssbruno goto parse_out; 827303628Ssbruno break; 828303628Ssbruno 829330168Seadler case IWM_UCODE_TLV_PAGING: 830330168Seadler if (tlv_len != sizeof(uint32_t)) { 831330168Seadler error = EINVAL; 832330168Seadler goto parse_out; 833330168Seadler } 834330784Seadler paging_mem_size = le32toh(*(const uint32_t *)tlv_data); 835330168Seadler 836330168Seadler IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 837330168Seadler "%s: Paging: paging enabled (size = %u bytes)\n", 838330168Seadler __func__, paging_mem_size); 839330168Seadler if (paging_mem_size > IWM_MAX_PAGING_IMAGE_SIZE) { 840330168Seadler device_printf(sc->sc_dev, 841330168Seadler "%s: Paging: driver supports up to %u bytes for paging image\n", 842330168Seadler __func__, IWM_MAX_PAGING_IMAGE_SIZE); 843330168Seadler error = EINVAL; 844330168Seadler goto out; 845330168Seadler } 846330168Seadler if (paging_mem_size & (IWM_FW_PAGING_SIZE - 1)) { 847330168Seadler device_printf(sc->sc_dev, 848330168Seadler "%s: Paging: image isn't multiple %u\n", 849330168Seadler __func__, IWM_FW_PAGING_SIZE); 850330168Seadler error = EINVAL; 851330168Seadler goto out; 852330168Seadler } 853330168Seadler 854330168Seadler sc->sc_fw.fw_sects[IWM_UCODE_REGULAR].paging_mem_size = 855330168Seadler paging_mem_size; 856330168Seadler usniffer_img = IWM_UCODE_REGULAR_USNIFFER; 857330168Seadler sc->sc_fw.fw_sects[usniffer_img].paging_mem_size = 858330168Seadler paging_mem_size; 859330168Seadler break; 860330168Seadler 861303628Ssbruno case IWM_UCODE_TLV_N_SCAN_CHANNELS: 862303628Ssbruno if (tlv_len != sizeof(uint32_t)) { 863303628Ssbruno error = EINVAL; 864303628Ssbruno goto parse_out; 865303628Ssbruno } 866330196Seadler capa->n_scan_channels = 867330784Seadler le32toh(*(const uint32_t *)tlv_data); 868303628Ssbruno break; 869303628Ssbruno 870303628Ssbruno case IWM_UCODE_TLV_FW_VERSION: 871303628Ssbruno if (tlv_len != sizeof(uint32_t) * 3) { 872303628Ssbruno error = EINVAL; 873303628Ssbruno goto parse_out; 874303628Ssbruno } 875303628Ssbruno snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), 876303628Ssbruno "%d.%d.%d", 877303628Ssbruno le32toh(((const uint32_t *)tlv_data)[0]), 878303628Ssbruno le32toh(((const uint32_t *)tlv_data)[1]), 879303628Ssbruno le32toh(((const uint32_t *)tlv_data)[2])); 880303628Ssbruno break; 881303628Ssbruno 882330179Seadler case IWM_UCODE_TLV_FW_MEM_SEG: 883330179Seadler break; 884330179Seadler 885286441Srpaulo default: 886286441Srpaulo device_printf(sc->sc_dev, 887286441Srpaulo "%s: unknown firmware section %d, abort\n", 888286441Srpaulo __func__, tlv_type); 889286441Srpaulo error = EINVAL; 890286441Srpaulo goto parse_out; 891286441Srpaulo } 892330784Seadler 893330784Seadler len -= roundup(tlv_len, 4); 894330784Seadler data += roundup(tlv_len, 4); 895286441Srpaulo } 896286441Srpaulo 897286441Srpaulo KASSERT(error == 0, ("unhandled error")); 898286441Srpaulo 899286441Srpaulo parse_out: 900286441Srpaulo if (error) { 901286441Srpaulo device_printf(sc->sc_dev, "firmware parse error %d, " 902286441Srpaulo "section type %d\n", error, tlv_type); 903286441Srpaulo } 904286441Srpaulo 905286441Srpaulo out: 906286441Srpaulo if (error) { 907286441Srpaulo fw->fw_status = IWM_FW_STATUS_NONE; 908293177Savos if (fw->fw_fp != NULL) 909286441Srpaulo iwm_fw_info_free(fw); 910286441Srpaulo } else 911286441Srpaulo fw->fw_status = IWM_FW_STATUS_DONE; 912286441Srpaulo wakeup(&sc->sc_fw); 913286441Srpaulo 914286441Srpaulo return error; 915286441Srpaulo} 916286441Srpaulo 917286441Srpaulo/* 918286441Srpaulo * DMA resource routines 919286441Srpaulo */ 920286441Srpaulo 921286441Srpaulo/* fwmem is used to load firmware onto the card */ 922286441Srpaulostatic int 923286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc) 924286441Srpaulo{ 925286441Srpaulo /* Must be aligned on a 16-byte boundary. */ 926286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, 927330185Seadler IWM_FH_MEM_TB_MAX_LENGTH, 16); 928286441Srpaulo} 929286441Srpaulo 930286441Srpaulo/* tx scheduler rings. not used? */ 931286441Srpaulostatic int 932286441Srpauloiwm_alloc_sched(struct iwm_softc *sc) 933286441Srpaulo{ 934286441Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 935303628Ssbruno return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, 936286441Srpaulo nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); 937286441Srpaulo} 938286441Srpaulo 939286441Srpaulo/* keep-warm page is used internally by the card. see iwl-fh.h for more info */ 940286441Srpaulostatic int 941286441Srpauloiwm_alloc_kw(struct iwm_softc *sc) 942286441Srpaulo{ 943286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); 944286441Srpaulo} 945286441Srpaulo 946286441Srpaulo/* interrupt cause table */ 947286441Srpaulostatic int 948286441Srpauloiwm_alloc_ict(struct iwm_softc *sc) 949286441Srpaulo{ 950286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, 951286441Srpaulo IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); 952286441Srpaulo} 953286441Srpaulo 954286441Srpaulostatic int 955286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 956286441Srpaulo{ 957286441Srpaulo bus_size_t size; 958286441Srpaulo int i, error; 959286441Srpaulo 960286441Srpaulo ring->cur = 0; 961286441Srpaulo 962286441Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 963286441Srpaulo size = IWM_RX_RING_COUNT * sizeof(uint32_t); 964286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 965286441Srpaulo if (error != 0) { 966286441Srpaulo device_printf(sc->sc_dev, 967286441Srpaulo "could not allocate RX ring DMA memory\n"); 968286441Srpaulo goto fail; 969286441Srpaulo } 970286441Srpaulo ring->desc = ring->desc_dma.vaddr; 971286441Srpaulo 972286441Srpaulo /* Allocate RX status area (16-byte aligned). */ 973286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, 974286441Srpaulo sizeof(*ring->stat), 16); 975286441Srpaulo if (error != 0) { 976286441Srpaulo device_printf(sc->sc_dev, 977286441Srpaulo "could not allocate RX status DMA memory\n"); 978286441Srpaulo goto fail; 979286441Srpaulo } 980286441Srpaulo ring->stat = ring->stat_dma.vaddr; 981286441Srpaulo 982286441Srpaulo /* Create RX buffer DMA tag. */ 983286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 984286441Srpaulo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 985289679Skevlo IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat); 986286441Srpaulo if (error != 0) { 987286441Srpaulo device_printf(sc->sc_dev, 988286441Srpaulo "%s: could not create RX buf DMA tag, error %d\n", 989286441Srpaulo __func__, error); 990286441Srpaulo goto fail; 991286441Srpaulo } 992286441Srpaulo 993301845Sadrian /* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */ 994301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map); 995301845Sadrian if (error != 0) { 996301845Sadrian device_printf(sc->sc_dev, 997301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 998301845Sadrian __func__, error); 999301845Sadrian goto fail; 1000301845Sadrian } 1001286441Srpaulo /* 1002286441Srpaulo * Allocate and map RX buffers. 1003286441Srpaulo */ 1004286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 1005301845Sadrian struct iwm_rx_data *data = &ring->data[i]; 1006301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1007301845Sadrian if (error != 0) { 1008301845Sadrian device_printf(sc->sc_dev, 1009301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 1010301845Sadrian __func__, error); 1011301845Sadrian goto fail; 1012301845Sadrian } 1013301845Sadrian data->m = NULL; 1014301845Sadrian 1015286441Srpaulo if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) { 1016286441Srpaulo goto fail; 1017286441Srpaulo } 1018286441Srpaulo } 1019286441Srpaulo return 0; 1020286441Srpaulo 1021286441Srpaulofail: iwm_free_rx_ring(sc, ring); 1022286441Srpaulo return error; 1023286441Srpaulo} 1024286441Srpaulo 1025286441Srpaulostatic void 1026301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1027301191Sadrian{ 1028287965Sadrian /* Reset the ring state */ 1029286441Srpaulo ring->cur = 0; 1030303628Ssbruno 1031303628Ssbruno /* 1032303628Ssbruno * The hw rx ring index in shared memory must also be cleared, 1033303628Ssbruno * otherwise the discrepancy can cause reprocessing chaos. 1034303628Ssbruno */ 1035346035Skevans if (sc->rxq.stat) 1036346035Skevans memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1037286441Srpaulo} 1038286441Srpaulo 1039286441Srpaulostatic void 1040286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1041286441Srpaulo{ 1042286441Srpaulo int i; 1043286441Srpaulo 1044286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1045286441Srpaulo iwm_dma_contig_free(&ring->stat_dma); 1046286441Srpaulo 1047286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 1048286441Srpaulo struct iwm_rx_data *data = &ring->data[i]; 1049286441Srpaulo 1050286441Srpaulo if (data->m != NULL) { 1051286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1052286441Srpaulo BUS_DMASYNC_POSTREAD); 1053286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1054286441Srpaulo m_freem(data->m); 1055286441Srpaulo data->m = NULL; 1056286441Srpaulo } 1057286441Srpaulo if (data->map != NULL) { 1058286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1059286441Srpaulo data->map = NULL; 1060286441Srpaulo } 1061286441Srpaulo } 1062301845Sadrian if (ring->spare_map != NULL) { 1063301845Sadrian bus_dmamap_destroy(ring->data_dmat, ring->spare_map); 1064301845Sadrian ring->spare_map = NULL; 1065301845Sadrian } 1066286441Srpaulo if (ring->data_dmat != NULL) { 1067286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1068286441Srpaulo ring->data_dmat = NULL; 1069286441Srpaulo } 1070286441Srpaulo} 1071286441Srpaulo 1072286441Srpaulostatic int 1073286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) 1074286441Srpaulo{ 1075286441Srpaulo bus_addr_t paddr; 1076286441Srpaulo bus_size_t size; 1077302104Sadrian size_t maxsize; 1078302104Sadrian int nsegments; 1079286441Srpaulo int i, error; 1080286441Srpaulo 1081286441Srpaulo ring->qid = qid; 1082286441Srpaulo ring->queued = 0; 1083286441Srpaulo ring->cur = 0; 1084286441Srpaulo 1085286441Srpaulo /* Allocate TX descriptors (256-byte aligned). */ 1086286441Srpaulo size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd); 1087286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 1088286441Srpaulo if (error != 0) { 1089286441Srpaulo device_printf(sc->sc_dev, 1090286441Srpaulo "could not allocate TX ring DMA memory\n"); 1091286441Srpaulo goto fail; 1092286441Srpaulo } 1093286441Srpaulo ring->desc = ring->desc_dma.vaddr; 1094286441Srpaulo 1095286441Srpaulo /* 1096286441Srpaulo * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need 1097286441Srpaulo * to allocate commands space for other rings. 1098286441Srpaulo */ 1099286441Srpaulo if (qid > IWM_MVM_CMD_QUEUE) 1100286441Srpaulo return 0; 1101286441Srpaulo 1102286441Srpaulo size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd); 1103286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4); 1104286441Srpaulo if (error != 0) { 1105286441Srpaulo device_printf(sc->sc_dev, 1106286441Srpaulo "could not allocate TX cmd DMA memory\n"); 1107286441Srpaulo goto fail; 1108286441Srpaulo } 1109286441Srpaulo ring->cmd = ring->cmd_dma.vaddr; 1110286441Srpaulo 1111302104Sadrian /* FW commands may require more mapped space than packets. */ 1112302104Sadrian if (qid == IWM_MVM_CMD_QUEUE) { 1113302104Sadrian maxsize = IWM_RBUF_SIZE; 1114302104Sadrian nsegments = 1; 1115302104Sadrian } else { 1116302104Sadrian maxsize = MCLBYTES; 1117302104Sadrian nsegments = IWM_MAX_SCATTER - 2; 1118302104Sadrian } 1119302104Sadrian 1120286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 1121302104Sadrian BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, 1122302104Sadrian nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat); 1123286441Srpaulo if (error != 0) { 1124286441Srpaulo device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); 1125286441Srpaulo goto fail; 1126286441Srpaulo } 1127286441Srpaulo 1128286441Srpaulo paddr = ring->cmd_dma.paddr; 1129286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1130286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1131286441Srpaulo 1132286441Srpaulo data->cmd_paddr = paddr; 1133286441Srpaulo data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) 1134286441Srpaulo + offsetof(struct iwm_tx_cmd, scratch); 1135286441Srpaulo paddr += sizeof(struct iwm_device_cmd); 1136286441Srpaulo 1137286441Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1138286441Srpaulo if (error != 0) { 1139286441Srpaulo device_printf(sc->sc_dev, 1140286441Srpaulo "could not create TX buf DMA map\n"); 1141286441Srpaulo goto fail; 1142286441Srpaulo } 1143286441Srpaulo } 1144286441Srpaulo KASSERT(paddr == ring->cmd_dma.paddr + size, 1145286441Srpaulo ("invalid physical address")); 1146286441Srpaulo return 0; 1147286441Srpaulo 1148286441Srpaulofail: iwm_free_tx_ring(sc, ring); 1149286441Srpaulo return error; 1150286441Srpaulo} 1151286441Srpaulo 1152286441Srpaulostatic void 1153286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1154286441Srpaulo{ 1155286441Srpaulo int i; 1156286441Srpaulo 1157286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1158286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1159286441Srpaulo 1160286441Srpaulo if (data->m != NULL) { 1161286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1162286441Srpaulo BUS_DMASYNC_POSTWRITE); 1163286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1164286441Srpaulo m_freem(data->m); 1165286441Srpaulo data->m = NULL; 1166286441Srpaulo } 1167286441Srpaulo } 1168286441Srpaulo /* Clear TX descriptors. */ 1169286441Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1170286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1171286441Srpaulo BUS_DMASYNC_PREWRITE); 1172286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1173286441Srpaulo ring->queued = 0; 1174286441Srpaulo ring->cur = 0; 1175330175Seadler 1176330175Seadler if (ring->qid == IWM_MVM_CMD_QUEUE && sc->cmd_hold_nic_awake) 1177330175Seadler iwm_pcie_clear_cmd_in_flight(sc); 1178286441Srpaulo} 1179286441Srpaulo 1180286441Srpaulostatic void 1181286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1182286441Srpaulo{ 1183286441Srpaulo int i; 1184286441Srpaulo 1185286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1186286441Srpaulo iwm_dma_contig_free(&ring->cmd_dma); 1187286441Srpaulo 1188286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1189286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1190286441Srpaulo 1191286441Srpaulo if (data->m != NULL) { 1192286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1193286441Srpaulo BUS_DMASYNC_POSTWRITE); 1194286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1195286441Srpaulo m_freem(data->m); 1196286441Srpaulo data->m = NULL; 1197286441Srpaulo } 1198286441Srpaulo if (data->map != NULL) { 1199286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1200286441Srpaulo data->map = NULL; 1201286441Srpaulo } 1202286441Srpaulo } 1203286441Srpaulo if (ring->data_dmat != NULL) { 1204286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1205286441Srpaulo ring->data_dmat = NULL; 1206286441Srpaulo } 1207286441Srpaulo} 1208286441Srpaulo 1209286441Srpaulo/* 1210286441Srpaulo * High-level hardware frobbing routines 1211286441Srpaulo */ 1212286441Srpaulo 1213286441Srpaulostatic void 1214286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc) 1215286441Srpaulo{ 1216286441Srpaulo sc->sc_intmask = IWM_CSR_INI_SET_MASK; 1217286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1218286441Srpaulo} 1219286441Srpaulo 1220286441Srpaulostatic void 1221286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc) 1222286441Srpaulo{ 1223286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1224286441Srpaulo} 1225286441Srpaulo 1226286441Srpaulostatic void 1227286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc) 1228286441Srpaulo{ 1229286441Srpaulo /* disable interrupts */ 1230286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 1231286441Srpaulo 1232286441Srpaulo /* acknowledge all interrupts */ 1233286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1234286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0); 1235286441Srpaulo} 1236286441Srpaulo 1237286441Srpaulostatic void 1238286441Srpauloiwm_ict_reset(struct iwm_softc *sc) 1239286441Srpaulo{ 1240286441Srpaulo iwm_disable_interrupts(sc); 1241286441Srpaulo 1242286441Srpaulo /* Reset ICT table. */ 1243286441Srpaulo memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE); 1244286441Srpaulo sc->ict_cur = 0; 1245286441Srpaulo 1246286441Srpaulo /* Set physical address of ICT table (4KB aligned). */ 1247286441Srpaulo IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, 1248286441Srpaulo IWM_CSR_DRAM_INT_TBL_ENABLE 1249303628Ssbruno | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER 1250286441Srpaulo | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK 1251286441Srpaulo | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); 1252286441Srpaulo 1253286441Srpaulo /* Switch to ICT interrupt mode in driver. */ 1254286441Srpaulo sc->sc_flags |= IWM_FLAG_USE_ICT; 1255286441Srpaulo 1256286441Srpaulo /* Re-enable interrupts. */ 1257286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1258286441Srpaulo iwm_enable_interrupts(sc); 1259286441Srpaulo} 1260286441Srpaulo 1261286441Srpaulo/* iwlwifi pcie/trans.c */ 1262286441Srpaulo 1263286441Srpaulo/* 1264286441Srpaulo * Since this .. hard-resets things, it's time to actually 1265286441Srpaulo * mark the first vap (if any) as having no mac context. 1266286441Srpaulo * It's annoying, but since the driver is potentially being 1267286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we 1268286441Srpaulo * have to correctly track this. 1269286441Srpaulo */ 1270286441Srpaulostatic void 1271286441Srpauloiwm_stop_device(struct iwm_softc *sc) 1272286441Srpaulo{ 1273287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1274286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1275303628Ssbruno int chnl, qid; 1276303628Ssbruno uint32_t mask = 0; 1277286441Srpaulo 1278286441Srpaulo /* tell the device to stop sending interrupts */ 1279286441Srpaulo iwm_disable_interrupts(sc); 1280286441Srpaulo 1281286441Srpaulo /* 1282286441Srpaulo * FreeBSD-local: mark the first vap as not-uploaded, 1283286441Srpaulo * so the next transition through auth/assoc 1284286441Srpaulo * will correctly populate the MAC context. 1285286441Srpaulo */ 1286286441Srpaulo if (vap) { 1287286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 1288330203Seadler iv->phy_ctxt = NULL; 1289286441Srpaulo iv->is_uploaded = 0; 1290286441Srpaulo } 1291286441Srpaulo 1292286441Srpaulo /* device going down, Stop using ICT table */ 1293286441Srpaulo sc->sc_flags &= ~IWM_FLAG_USE_ICT; 1294286441Srpaulo 1295286441Srpaulo /* stop tx and rx. tx and rx bits, as usual, are from if_iwn */ 1296286441Srpaulo 1297330199Seadler if (iwm_nic_lock(sc)) { 1298330199Seadler iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1299286441Srpaulo 1300303628Ssbruno /* Stop each Tx DMA channel */ 1301286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1302286441Srpaulo IWM_WRITE(sc, 1303286441Srpaulo IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0); 1304303628Ssbruno mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl); 1305303628Ssbruno } 1306286441Srpaulo 1307303628Ssbruno /* Wait for DMA channels to be idle */ 1308303628Ssbruno if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask, 1309303628Ssbruno 5000)) { 1310303628Ssbruno device_printf(sc->sc_dev, 1311303628Ssbruno "Failing on timeout while stopping DMA channel: [0x%08x]\n", 1312303628Ssbruno IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG)); 1313286441Srpaulo } 1314286441Srpaulo iwm_nic_unlock(sc); 1315286441Srpaulo } 1316330172Seadler iwm_pcie_rx_stop(sc); 1317286441Srpaulo 1318286441Srpaulo /* Stop RX ring. */ 1319286441Srpaulo iwm_reset_rx_ring(sc, &sc->rxq); 1320286441Srpaulo 1321286441Srpaulo /* Reset all TX rings. */ 1322286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) 1323286441Srpaulo iwm_reset_tx_ring(sc, &sc->txq[qid]); 1324286441Srpaulo 1325330176Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 1326330176Seadler /* Power-down device's busmaster DMA clocks */ 1327330199Seadler if (iwm_nic_lock(sc)) { 1328330199Seadler iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, 1329330199Seadler IWM_APMG_CLK_VAL_DMA_CLK_RQT); 1330330200Seadler iwm_nic_unlock(sc); 1331330199Seadler } 1332330176Seadler DELAY(5); 1333330176Seadler } 1334286441Srpaulo 1335286441Srpaulo /* Make sure (redundant) we've released our request to stay awake */ 1336286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 1337286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 1338286441Srpaulo 1339286441Srpaulo /* Stop the device, and put it in low power state */ 1340286441Srpaulo iwm_apm_stop(sc); 1341286441Srpaulo 1342286441Srpaulo /* Upon stop, the APM issues an interrupt if HW RF kill is set. 1343286441Srpaulo * Clean again the interrupt here 1344286441Srpaulo */ 1345286441Srpaulo iwm_disable_interrupts(sc); 1346286441Srpaulo /* stop and reset the on-board processor */ 1347303628Ssbruno IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); 1348286441Srpaulo 1349286441Srpaulo /* 1350286441Srpaulo * Even if we stop the HW, we still want the RF kill 1351286441Srpaulo * interrupt 1352286441Srpaulo */ 1353286441Srpaulo iwm_enable_rfkill_int(sc); 1354286441Srpaulo iwm_check_rfkill(sc); 1355286441Srpaulo} 1356286441Srpaulo 1357286441Srpaulo/* iwlwifi: mvm/ops.c */ 1358286441Srpaulostatic void 1359286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc) 1360286441Srpaulo{ 1361286441Srpaulo uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash; 1362286441Srpaulo uint32_t reg_val = 0; 1363330167Seadler uint32_t phy_config = iwm_mvm_get_phy_config(sc); 1364286441Srpaulo 1365330167Seadler radio_cfg_type = (phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >> 1366286441Srpaulo IWM_FW_PHY_CFG_RADIO_TYPE_POS; 1367330167Seadler radio_cfg_step = (phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >> 1368286441Srpaulo IWM_FW_PHY_CFG_RADIO_STEP_POS; 1369330167Seadler radio_cfg_dash = (phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >> 1370286441Srpaulo IWM_FW_PHY_CFG_RADIO_DASH_POS; 1371286441Srpaulo 1372286441Srpaulo /* SKU control */ 1373286441Srpaulo reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) << 1374286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; 1375286441Srpaulo reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) << 1376286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; 1377286441Srpaulo 1378286441Srpaulo /* radio configuration */ 1379286441Srpaulo reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; 1380286441Srpaulo reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; 1381286441Srpaulo reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; 1382286441Srpaulo 1383286441Srpaulo IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val); 1384286441Srpaulo 1385286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1386286441Srpaulo "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, 1387286441Srpaulo radio_cfg_step, radio_cfg_dash); 1388286441Srpaulo 1389286441Srpaulo /* 1390286441Srpaulo * W/A : NIC is stuck in a reset state after Early PCIe power off 1391286441Srpaulo * (PCIe power is lost before PERST# is asserted), causing ME FW 1392286441Srpaulo * to lose ownership and not being able to obtain it back. 1393286441Srpaulo */ 1394330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 1395303628Ssbruno iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, 1396303628Ssbruno IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, 1397303628Ssbruno ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); 1398303628Ssbruno } 1399286441Srpaulo} 1400286441Srpaulo 1401286441Srpaulostatic int 1402286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc) 1403286441Srpaulo{ 1404286441Srpaulo /* 1405286441Srpaulo * Initialize RX ring. This is from the iwn driver. 1406286441Srpaulo */ 1407286441Srpaulo memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1408286441Srpaulo 1409330172Seadler /* Stop Rx DMA */ 1410330172Seadler iwm_pcie_rx_stop(sc); 1411330172Seadler 1412330172Seadler if (!iwm_nic_lock(sc)) 1413330172Seadler return EBUSY; 1414330172Seadler 1415330172Seadler /* reset and flush pointers */ 1416286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); 1417286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); 1418286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0); 1419286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 1420286441Srpaulo 1421286441Srpaulo /* Set physical address of RX ring (256-byte aligned). */ 1422286441Srpaulo IWM_WRITE(sc, 1423286441Srpaulo IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8); 1424286441Srpaulo 1425286441Srpaulo /* Set physical address of RX status (16-byte aligned). */ 1426286441Srpaulo IWM_WRITE(sc, 1427286441Srpaulo IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); 1428286441Srpaulo 1429330208Seadler /* Enable Rx DMA 1430330208Seadler * XXX 5000 HW isn't supported by the iwm(4) driver. 1431330208Seadler * IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY is set because of HW bug in 1432330208Seadler * the credit mechanism in 5000 HW RX FIFO 1433330208Seadler * Direct rx interrupts to hosts 1434330208Seadler * Rx buffer size 4 or 8k or 12k 1435330208Seadler * RB timeout 0x10 1436330208Seadler * 256 RBDs 1437330208Seadler */ 1438286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 1439286441Srpaulo IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 1440286441Srpaulo IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ 1441286441Srpaulo IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 1442330208Seadler IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | 1443303628Ssbruno (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | 1444286441Srpaulo IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); 1445286441Srpaulo 1446286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF); 1447286441Srpaulo 1448286441Srpaulo /* W/A for interrupt coalescing bug in 7260 and 3160 */ 1449330166Seadler if (sc->cfg->host_interrupt_operation_mode) 1450286441Srpaulo IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE); 1451286441Srpaulo 1452286441Srpaulo /* 1453286441Srpaulo * Thus sayeth el jefe (iwlwifi) via a comment: 1454286441Srpaulo * 1455286441Srpaulo * This value should initially be 0 (before preparing any 1456303628Ssbruno * RBs), should be 8 after preparing the first 8 RBs (for example) 1457286441Srpaulo */ 1458286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); 1459286441Srpaulo 1460286441Srpaulo iwm_nic_unlock(sc); 1461286441Srpaulo 1462286441Srpaulo return 0; 1463286441Srpaulo} 1464286441Srpaulo 1465286441Srpaulostatic int 1466286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc) 1467286441Srpaulo{ 1468286441Srpaulo int qid; 1469286441Srpaulo 1470286441Srpaulo if (!iwm_nic_lock(sc)) 1471286441Srpaulo return EBUSY; 1472286441Srpaulo 1473286441Srpaulo /* Deactivate TX scheduler. */ 1474286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1475286441Srpaulo 1476286441Srpaulo /* Set physical address of "keep warm" page (16-byte aligned). */ 1477286441Srpaulo IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4); 1478286441Srpaulo 1479286441Srpaulo /* Initialize TX rings. */ 1480286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) { 1481286441Srpaulo struct iwm_tx_ring *txq = &sc->txq[qid]; 1482286441Srpaulo 1483286441Srpaulo /* Set physical address of TX ring (256-byte aligned). */ 1484286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid), 1485286441Srpaulo txq->desc_dma.paddr >> 8); 1486286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 1487286441Srpaulo "%s: loading ring %d descriptors (%p) at %lx\n", 1488286441Srpaulo __func__, 1489286441Srpaulo qid, txq->desc, 1490286441Srpaulo (unsigned long) (txq->desc_dma.paddr >> 8)); 1491286441Srpaulo } 1492303628Ssbruno 1493303628Ssbruno iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); 1494303628Ssbruno 1495286441Srpaulo iwm_nic_unlock(sc); 1496286441Srpaulo 1497286441Srpaulo return 0; 1498286441Srpaulo} 1499286441Srpaulo 1500286441Srpaulostatic int 1501286441Srpauloiwm_nic_init(struct iwm_softc *sc) 1502286441Srpaulo{ 1503286441Srpaulo int error; 1504286441Srpaulo 1505286441Srpaulo iwm_apm_init(sc); 1506330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 1507303628Ssbruno iwm_set_pwr(sc); 1508286441Srpaulo 1509286441Srpaulo iwm_mvm_nic_config(sc); 1510286441Srpaulo 1511286441Srpaulo if ((error = iwm_nic_rx_init(sc)) != 0) 1512286441Srpaulo return error; 1513286441Srpaulo 1514286441Srpaulo /* 1515286441Srpaulo * Ditto for TX, from iwn 1516286441Srpaulo */ 1517286441Srpaulo if ((error = iwm_nic_tx_init(sc)) != 0) 1518286441Srpaulo return error; 1519286441Srpaulo 1520286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1521286441Srpaulo "%s: shadow registers enabled\n", __func__); 1522286441Srpaulo IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff); 1523286441Srpaulo 1524286441Srpaulo return 0; 1525286441Srpaulo} 1526286441Srpaulo 1527330210Seadlerint 1528303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) 1529286441Srpaulo{ 1530286441Srpaulo if (!iwm_nic_lock(sc)) { 1531286441Srpaulo device_printf(sc->sc_dev, 1532286441Srpaulo "%s: cannot enable txq %d\n", 1533286441Srpaulo __func__, 1534286441Srpaulo qid); 1535303628Ssbruno return EBUSY; 1536286441Srpaulo } 1537286441Srpaulo 1538303628Ssbruno IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); 1539286441Srpaulo 1540303628Ssbruno if (qid == IWM_MVM_CMD_QUEUE) { 1541303628Ssbruno /* unactivate before configuration */ 1542303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1543303628Ssbruno (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) 1544303628Ssbruno | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); 1545286441Srpaulo 1546330183Seadler iwm_nic_unlock(sc); 1547330183Seadler 1548303628Ssbruno iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); 1549286441Srpaulo 1550330183Seadler if (!iwm_nic_lock(sc)) { 1551330183Seadler device_printf(sc->sc_dev, 1552330183Seadler "%s: cannot enable txq %d\n", __func__, qid); 1553330183Seadler return EBUSY; 1554330183Seadler } 1555303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); 1556330183Seadler iwm_nic_unlock(sc); 1557286441Srpaulo 1558330183Seadler iwm_write_mem32(sc, sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); 1559303628Ssbruno /* Set scheduler window size and frame limit. */ 1560303628Ssbruno iwm_write_mem32(sc, 1561330183Seadler sc->scd_base_addr + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + 1562303628Ssbruno sizeof(uint32_t), 1563303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & 1564303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | 1565303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 1566303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); 1567286441Srpaulo 1568330183Seadler if (!iwm_nic_lock(sc)) { 1569330183Seadler device_printf(sc->sc_dev, 1570330183Seadler "%s: cannot enable txq %d\n", __func__, qid); 1571330183Seadler return EBUSY; 1572330183Seadler } 1573303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1574303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 1575303628Ssbruno (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | 1576303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | 1577303628Ssbruno IWM_SCD_QUEUE_STTS_REG_MSK); 1578303628Ssbruno } else { 1579303628Ssbruno struct iwm_scd_txq_cfg_cmd cmd; 1580303628Ssbruno int error; 1581286441Srpaulo 1582303628Ssbruno iwm_nic_unlock(sc); 1583303628Ssbruno 1584303628Ssbruno memset(&cmd, 0, sizeof(cmd)); 1585303628Ssbruno cmd.scd_queue = qid; 1586303628Ssbruno cmd.enable = 1; 1587303628Ssbruno cmd.sta_id = sta_id; 1588303628Ssbruno cmd.tx_fifo = fifo; 1589303628Ssbruno cmd.aggregate = 0; 1590303628Ssbruno cmd.window = IWM_FRAME_LIMIT; 1591303628Ssbruno 1592303628Ssbruno error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, 1593303628Ssbruno sizeof(cmd), &cmd); 1594303628Ssbruno if (error) { 1595303628Ssbruno device_printf(sc->sc_dev, 1596303628Ssbruno "cannot enable txq %d\n", qid); 1597303628Ssbruno return error; 1598303628Ssbruno } 1599303628Ssbruno 1600303628Ssbruno if (!iwm_nic_lock(sc)) 1601303628Ssbruno return EBUSY; 1602303628Ssbruno } 1603303628Ssbruno 1604303628Ssbruno iwm_write_prph(sc, IWM_SCD_EN_CTRL, 1605303628Ssbruno iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); 1606303628Ssbruno 1607286441Srpaulo iwm_nic_unlock(sc); 1608286441Srpaulo 1609303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n", 1610286441Srpaulo __func__, qid, fifo); 1611303628Ssbruno 1612303628Ssbruno return 0; 1613286441Srpaulo} 1614286441Srpaulo 1615286441Srpaulostatic int 1616330183Seadleriwm_trans_pcie_fw_alive(struct iwm_softc *sc, uint32_t scd_base_addr) 1617286441Srpaulo{ 1618286441Srpaulo int error, chnl; 1619286441Srpaulo 1620330183Seadler int clear_dwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - 1621330183Seadler IWM_SCD_CONTEXT_MEM_LOWER_BOUND) / sizeof(uint32_t); 1622330183Seadler 1623286441Srpaulo if (!iwm_nic_lock(sc)) 1624286441Srpaulo return EBUSY; 1625286441Srpaulo 1626330183Seadler iwm_ict_reset(sc); 1627330183Seadler 1628330183Seadler sc->scd_base_addr = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); 1629330183Seadler if (scd_base_addr != 0 && 1630330183Seadler scd_base_addr != sc->scd_base_addr) { 1631286441Srpaulo device_printf(sc->sc_dev, 1632303628Ssbruno "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", 1633330183Seadler __func__, sc->scd_base_addr, scd_base_addr); 1634286441Srpaulo } 1635286441Srpaulo 1636330199Seadler iwm_nic_unlock(sc); 1637330199Seadler 1638330183Seadler /* reset context data, TX status and translation data */ 1639286441Srpaulo error = iwm_write_mem(sc, 1640330183Seadler sc->scd_base_addr + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, 1641330183Seadler NULL, clear_dwords); 1642286441Srpaulo if (error) 1643330183Seadler return EBUSY; 1644286441Srpaulo 1645330183Seadler if (!iwm_nic_lock(sc)) 1646330183Seadler return EBUSY; 1647330183Seadler 1648286441Srpaulo /* Set physical address of TX scheduler rings (1KB aligned). */ 1649286441Srpaulo iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); 1650286441Srpaulo 1651286441Srpaulo iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); 1652286441Srpaulo 1653303628Ssbruno iwm_nic_unlock(sc); 1654303628Ssbruno 1655286441Srpaulo /* enable command channel */ 1656303628Ssbruno error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); 1657303628Ssbruno if (error) 1658303628Ssbruno return error; 1659286441Srpaulo 1660303628Ssbruno if (!iwm_nic_lock(sc)) 1661303628Ssbruno return EBUSY; 1662303628Ssbruno 1663286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); 1664286441Srpaulo 1665286441Srpaulo /* Enable DMA channels. */ 1666286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1667286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 1668286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 1669286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); 1670286441Srpaulo } 1671286441Srpaulo 1672286441Srpaulo IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, 1673286441Srpaulo IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); 1674286441Srpaulo 1675330183Seadler iwm_nic_unlock(sc); 1676330183Seadler 1677286441Srpaulo /* Enable L1-Active */ 1678330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 1679303628Ssbruno iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, 1680303628Ssbruno IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 1681303628Ssbruno } 1682286441Srpaulo 1683286441Srpaulo return error; 1684286441Srpaulo} 1685286441Srpaulo 1686286441Srpaulo/* 1687286441Srpaulo * NVM read access and content parsing. We do not support 1688286441Srpaulo * external NVM or writing NVM. 1689286441Srpaulo * iwlwifi/mvm/nvm.c 1690286441Srpaulo */ 1691286441Srpaulo 1692286441Srpaulo/* Default NVM size to read */ 1693303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) 1694286441Srpaulo 1695286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1 1696286441Srpaulo#define IWM_NVM_READ_OPCODE 0 1697286441Srpaulo 1698303628Ssbruno/* load nvm chunk response */ 1699330160Seadlerenum { 1700330160Seadler IWM_READ_NVM_CHUNK_SUCCEED = 0, 1701330160Seadler IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 1702330160Seadler}; 1703303628Ssbruno 1704286441Srpaulostatic int 1705286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, 1706286441Srpaulo uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) 1707286441Srpaulo{ 1708286441Srpaulo struct iwm_nvm_access_cmd nvm_access_cmd = { 1709286441Srpaulo .offset = htole16(offset), 1710286441Srpaulo .length = htole16(length), 1711286441Srpaulo .type = htole16(section), 1712286441Srpaulo .op_code = IWM_NVM_READ_OPCODE, 1713286441Srpaulo }; 1714286441Srpaulo struct iwm_nvm_access_resp *nvm_resp; 1715286441Srpaulo struct iwm_rx_packet *pkt; 1716286441Srpaulo struct iwm_host_cmd cmd = { 1717286441Srpaulo .id = IWM_NVM_ACCESS_CMD, 1718330160Seadler .flags = IWM_CMD_WANT_SKB | IWM_CMD_SEND_IN_RFKILL, 1719286441Srpaulo .data = { &nvm_access_cmd, }, 1720286441Srpaulo }; 1721330160Seadler int ret, bytes_read, offset_read; 1722286441Srpaulo uint8_t *resp_data; 1723286441Srpaulo 1724286441Srpaulo cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); 1725286441Srpaulo 1726286441Srpaulo ret = iwm_send_cmd(sc, &cmd); 1727303628Ssbruno if (ret) { 1728303628Ssbruno device_printf(sc->sc_dev, 1729303628Ssbruno "Could not send NVM_ACCESS command (error=%d)\n", ret); 1730286441Srpaulo return ret; 1731303628Ssbruno } 1732286441Srpaulo 1733286441Srpaulo pkt = cmd.resp_pkt; 1734286441Srpaulo 1735286441Srpaulo /* Extract NVM response */ 1736286441Srpaulo nvm_resp = (void *)pkt->data; 1737286441Srpaulo ret = le16toh(nvm_resp->status); 1738286441Srpaulo bytes_read = le16toh(nvm_resp->length); 1739286441Srpaulo offset_read = le16toh(nvm_resp->offset); 1740286441Srpaulo resp_data = nvm_resp->data; 1741286441Srpaulo if (ret) { 1742330160Seadler if ((offset != 0) && 1743330160Seadler (ret == IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { 1744330160Seadler /* 1745330160Seadler * meaning of NOT_VALID_ADDRESS: 1746330160Seadler * driver try to read chunk from address that is 1747330160Seadler * multiple of 2K and got an error since addr is empty. 1748330160Seadler * meaning of (offset != 0): driver already 1749330160Seadler * read valid data from another chunk so this case 1750330160Seadler * is not an error. 1751330160Seadler */ 1752330160Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1753330160Seadler "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", 1754330160Seadler offset); 1755330160Seadler *len = 0; 1756330160Seadler ret = 0; 1757330160Seadler } else { 1758330160Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1759330160Seadler "NVM access command failed with status %d\n", ret); 1760330160Seadler ret = EIO; 1761330160Seadler } 1762286441Srpaulo goto exit; 1763286441Srpaulo } 1764286441Srpaulo 1765286441Srpaulo if (offset_read != offset) { 1766286441Srpaulo device_printf(sc->sc_dev, 1767303628Ssbruno "NVM ACCESS response with invalid offset %d\n", 1768303628Ssbruno offset_read); 1769286441Srpaulo ret = EINVAL; 1770286441Srpaulo goto exit; 1771286441Srpaulo } 1772286441Srpaulo 1773303628Ssbruno if (bytes_read > length) { 1774303628Ssbruno device_printf(sc->sc_dev, 1775303628Ssbruno "NVM ACCESS response with too much data " 1776330160Seadler "(%d bytes requested, %d bytes received)\n", 1777303628Ssbruno length, bytes_read); 1778303628Ssbruno ret = EINVAL; 1779303628Ssbruno goto exit; 1780303628Ssbruno } 1781303628Ssbruno 1782330165Seadler /* Write data to NVM */ 1783286441Srpaulo memcpy(data + offset, resp_data, bytes_read); 1784286441Srpaulo *len = bytes_read; 1785286441Srpaulo 1786286441Srpaulo exit: 1787286441Srpaulo iwm_free_resp(sc, &cmd); 1788286441Srpaulo return ret; 1789286441Srpaulo} 1790286441Srpaulo 1791286441Srpaulo/* 1792286441Srpaulo * Reads an NVM section completely. 1793303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read 1794286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited 1795286441Srpaulo * by uCode, we need to manually check in this case that we don't 1796286441Srpaulo * overflow and try to read more than the EEPROM size. 1797286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and 1798286441Srpaulo * the uCode fills the response with as much data as we can, 1799286441Srpaulo * without overflowing, so no check is needed. 1800286441Srpaulo */ 1801286441Srpaulostatic int 1802286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc, 1803330165Seadler uint16_t section, uint8_t *data, uint16_t *len, uint32_t size_read) 1804286441Srpaulo{ 1805330165Seadler uint16_t seglen, length, offset = 0; 1806330165Seadler int ret; 1807286441Srpaulo 1808330165Seadler /* Set nvm section read length */ 1809330165Seadler length = IWM_NVM_DEFAULT_CHUNK_SIZE; 1810303628Ssbruno 1811330165Seadler seglen = length; 1812286441Srpaulo 1813330165Seadler /* Read the NVM until exhausted (reading less than requested) */ 1814330165Seadler while (seglen == length) { 1815330165Seadler /* Check no memory assumptions fail and cause an overflow */ 1816330165Seadler if ((size_read + offset + length) > 1817330166Seadler sc->cfg->eeprom_size) { 1818330165Seadler device_printf(sc->sc_dev, 1819330165Seadler "EEPROM size is too small for NVM\n"); 1820330165Seadler return ENOBUFS; 1821286441Srpaulo } 1822330165Seadler 1823330165Seadler ret = iwm_nvm_read_chunk(sc, section, offset, length, data, &seglen); 1824330165Seadler if (ret) { 1825330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1826330165Seadler "Cannot read NVM from section %d offset %d, length %d\n", 1827330165Seadler section, offset, length); 1828330165Seadler return ret; 1829330165Seadler } 1830330165Seadler offset += seglen; 1831286441Srpaulo } 1832286441Srpaulo 1833330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1834330165Seadler "NVM section %d read completed\n", section); 1835330165Seadler *len = offset; 1836330165Seadler return 0; 1837286441Srpaulo} 1838286441Srpaulo 1839286441Srpaulo/* 1840286441Srpaulo * BEGIN IWM_NVM_PARSE 1841286441Srpaulo */ 1842286441Srpaulo 1843286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */ 1844286441Srpaulo 1845286441Srpaulo/* NVM offsets (in words) definitions */ 1846303628Ssbrunoenum iwm_nvm_offsets { 1847286441Srpaulo /* NVM HW-Section offset (in words) definitions */ 1848286441Srpaulo IWM_HW_ADDR = 0x15, 1849286441Srpaulo 1850286441Srpaulo/* NVM SW-Section offset (in words) definitions */ 1851286441Srpaulo IWM_NVM_SW_SECTION = 0x1C0, 1852286441Srpaulo IWM_NVM_VERSION = 0, 1853286441Srpaulo IWM_RADIO_CFG = 1, 1854286441Srpaulo IWM_SKU = 2, 1855286441Srpaulo IWM_N_HW_ADDRS = 3, 1856286441Srpaulo IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION, 1857286441Srpaulo 1858286441Srpaulo/* NVM calibration section offset (in words) definitions */ 1859286441Srpaulo IWM_NVM_CALIB_SECTION = 0x2B8, 1860286441Srpaulo IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION 1861286441Srpaulo}; 1862286441Srpaulo 1863303628Ssbrunoenum iwm_8000_nvm_offsets { 1864303628Ssbruno /* NVM HW-Section offset (in words) definitions */ 1865303628Ssbruno IWM_HW_ADDR0_WFPM_8000 = 0x12, 1866303628Ssbruno IWM_HW_ADDR1_WFPM_8000 = 0x16, 1867303628Ssbruno IWM_HW_ADDR0_PCIE_8000 = 0x8A, 1868303628Ssbruno IWM_HW_ADDR1_PCIE_8000 = 0x8E, 1869303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000 = 1, 1870303628Ssbruno 1871303628Ssbruno /* NVM SW-Section offset (in words) definitions */ 1872303628Ssbruno IWM_NVM_SW_SECTION_8000 = 0x1C0, 1873303628Ssbruno IWM_NVM_VERSION_8000 = 0, 1874303628Ssbruno IWM_RADIO_CFG_8000 = 0, 1875303628Ssbruno IWM_SKU_8000 = 2, 1876303628Ssbruno IWM_N_HW_ADDRS_8000 = 3, 1877303628Ssbruno 1878303628Ssbruno /* NVM REGULATORY -Section offset (in words) definitions */ 1879303628Ssbruno IWM_NVM_CHANNELS_8000 = 0, 1880303628Ssbruno IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7, 1881303628Ssbruno IWM_NVM_LAR_OFFSET_8000 = 0x507, 1882303628Ssbruno IWM_NVM_LAR_ENABLED_8000 = 0x7, 1883303628Ssbruno 1884303628Ssbruno /* NVM calibration section offset (in words) definitions */ 1885303628Ssbruno IWM_NVM_CALIB_SECTION_8000 = 0x2B8, 1886303628Ssbruno IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000 1887303628Ssbruno}; 1888303628Ssbruno 1889286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */ 1890286441Srpauloenum nvm_sku_bits { 1891286441Srpaulo IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), 1892286441Srpaulo IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), 1893286441Srpaulo IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), 1894286441Srpaulo IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), 1895286441Srpaulo}; 1896286441Srpaulo 1897286441Srpaulo/* radio config bits (actual values from NVM definition) */ 1898286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ 1899286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ 1900286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ 1901286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ 1902286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ 1903286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ 1904286441Srpaulo 1905303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x) (x & 0xF) 1906303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x) ((x >> 4) & 0xF) 1907303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x) ((x >> 8) & 0xF) 1908303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x) ((x >> 12) & 0xFFF) 1909303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) 1910303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) 1911303628Ssbruno 1912286441Srpaulo#define DEFAULT_MAX_TX_POWER 16 1913286441Srpaulo 1914286441Srpaulo/** 1915286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM 1916286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo 1917286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel 1918286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed 1919286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required 1920330165Seadler * XXX cannot find this (DFS) flag in iwm-nvm-parse.c 1921286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate 1922286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?) 1923286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) 1924286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) 1925286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) 1926286441Srpaulo */ 1927286441Srpauloenum iwm_nvm_channel_flags { 1928286441Srpaulo IWM_NVM_CHANNEL_VALID = (1 << 0), 1929286441Srpaulo IWM_NVM_CHANNEL_IBSS = (1 << 1), 1930286441Srpaulo IWM_NVM_CHANNEL_ACTIVE = (1 << 3), 1931286441Srpaulo IWM_NVM_CHANNEL_RADAR = (1 << 4), 1932286441Srpaulo IWM_NVM_CHANNEL_DFS = (1 << 7), 1933286441Srpaulo IWM_NVM_CHANNEL_WIDE = (1 << 8), 1934286441Srpaulo IWM_NVM_CHANNEL_40MHZ = (1 << 9), 1935286441Srpaulo IWM_NVM_CHANNEL_80MHZ = (1 << 10), 1936286441Srpaulo IWM_NVM_CHANNEL_160MHZ = (1 << 11), 1937286441Srpaulo}; 1938286441Srpaulo 1939286441Srpaulo/* 1940298877Savos * Translate EEPROM flags to net80211. 1941286441Srpaulo */ 1942298877Savosstatic uint32_t 1943298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags) 1944286441Srpaulo{ 1945298877Savos uint32_t nflags; 1946286441Srpaulo 1947298877Savos nflags = 0; 1948298877Savos if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0) 1949298877Savos nflags |= IEEE80211_CHAN_PASSIVE; 1950298877Savos if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0) 1951298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1952298877Savos if (ch_flags & IWM_NVM_CHANNEL_RADAR) { 1953298877Savos nflags |= IEEE80211_CHAN_DFS; 1954298877Savos /* Just in case. */ 1955298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1956286441Srpaulo } 1957286441Srpaulo 1958298877Savos return (nflags); 1959286441Srpaulo} 1960286441Srpaulo 1961286441Srpaulostatic void 1962298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[], 1963303628Ssbruno int maxchans, int *nchans, int ch_idx, size_t ch_num, 1964303628Ssbruno const uint8_t bands[]) 1965286441Srpaulo{ 1966330165Seadler const uint16_t * const nvm_ch_flags = sc->nvm_data->nvm_ch_flags; 1967298877Savos uint32_t nflags; 1968286441Srpaulo uint16_t ch_flags; 1969298877Savos uint8_t ieee; 1970298877Savos int error; 1971286441Srpaulo 1972298877Savos for (; ch_idx < ch_num; ch_idx++) { 1973286441Srpaulo ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); 1974330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 1975303628Ssbruno ieee = iwm_nvm_channels[ch_idx]; 1976303628Ssbruno else 1977303628Ssbruno ieee = iwm_nvm_channels_8000[ch_idx]; 1978286441Srpaulo 1979286441Srpaulo if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { 1980286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1981286441Srpaulo "Ch. %d Flags %x [%sGHz] - No traffic\n", 1982298877Savos ieee, ch_flags, 1983286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1984286441Srpaulo "5.2" : "2.4"); 1985286441Srpaulo continue; 1986286441Srpaulo } 1987286441Srpaulo 1988298877Savos nflags = iwm_eeprom_channel_flags(ch_flags); 1989298877Savos error = ieee80211_add_channel(chans, maxchans, nchans, 1990298877Savos ieee, 0, 0, nflags, bands); 1991298877Savos if (error != 0) 1992298877Savos break; 1993286441Srpaulo 1994286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1995286441Srpaulo "Ch. %d Flags %x [%sGHz] - Added\n", 1996298877Savos ieee, ch_flags, 1997286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1998286441Srpaulo "5.2" : "2.4"); 1999286441Srpaulo } 2000286441Srpaulo} 2001286441Srpaulo 2002298877Savosstatic void 2003298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans, 2004298877Savos struct ieee80211_channel chans[]) 2005298877Savos{ 2006298877Savos struct iwm_softc *sc = ic->ic_softc; 2007330165Seadler struct iwm_nvm_data *data = sc->nvm_data; 2008299883Skevlo uint8_t bands[IEEE80211_MODE_BYTES]; 2009303628Ssbruno size_t ch_num; 2010298877Savos 2011298877Savos memset(bands, 0, sizeof(bands)); 2012298877Savos /* 1-13: 11b/g channels. */ 2013298877Savos setbit(bands, IEEE80211_MODE_11B); 2014298877Savos setbit(bands, IEEE80211_MODE_11G); 2015298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 0, 2016298877Savos IWM_NUM_2GHZ_CHANNELS - 1, bands); 2017298877Savos 2018298877Savos /* 14: 11b channel only. */ 2019298877Savos clrbit(bands, IEEE80211_MODE_11G); 2020298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 2021298936Savos IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands); 2022298877Savos 2023298877Savos if (data->sku_cap_band_52GHz_enable) { 2024330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 2025303628Ssbruno ch_num = nitems(iwm_nvm_channels); 2026303628Ssbruno else 2027303628Ssbruno ch_num = nitems(iwm_nvm_channels_8000); 2028298877Savos memset(bands, 0, sizeof(bands)); 2029298877Savos setbit(bands, IEEE80211_MODE_11A); 2030298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 2031303628Ssbruno IWM_NUM_2GHZ_CHANNELS, ch_num, bands); 2032298877Savos } 2033298877Savos} 2034298877Savos 2035303628Ssbrunostatic void 2036330165Seadleriwm_set_hw_address_family_8000(struct iwm_softc *sc, struct iwm_nvm_data *data, 2037303628Ssbruno const uint16_t *mac_override, const uint16_t *nvm_hw) 2038303628Ssbruno{ 2039303628Ssbruno const uint8_t *hw_addr; 2040303628Ssbruno 2041303628Ssbruno if (mac_override) { 2042303628Ssbruno static const uint8_t reserved_mac[] = { 2043303628Ssbruno 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 2044303628Ssbruno }; 2045303628Ssbruno 2046303628Ssbruno hw_addr = (const uint8_t *)(mac_override + 2047303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000); 2048303628Ssbruno 2049303628Ssbruno /* 2050303628Ssbruno * Store the MAC address from MAO section. 2051303628Ssbruno * No byte swapping is required in MAO section 2052303628Ssbruno */ 2053303628Ssbruno IEEE80211_ADDR_COPY(data->hw_addr, hw_addr); 2054303628Ssbruno 2055303628Ssbruno /* 2056303628Ssbruno * Force the use of the OTP MAC address in case of reserved MAC 2057303628Ssbruno * address in the NVM, or if address is given but invalid. 2058303628Ssbruno */ 2059303628Ssbruno if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) && 2060303628Ssbruno !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) && 2061303628Ssbruno iwm_is_valid_ether_addr(data->hw_addr) && 2062303628Ssbruno !IEEE80211_IS_MULTICAST(data->hw_addr)) 2063303628Ssbruno return; 2064303628Ssbruno 2065303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2066303628Ssbruno "%s: mac address from nvm override section invalid\n", 2067303628Ssbruno __func__); 2068303628Ssbruno } 2069303628Ssbruno 2070303628Ssbruno if (nvm_hw) { 2071303628Ssbruno /* read the mac address from WFMP registers */ 2072303628Ssbruno uint32_t mac_addr0 = 2073303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0)); 2074303628Ssbruno uint32_t mac_addr1 = 2075303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1)); 2076303628Ssbruno 2077303628Ssbruno hw_addr = (const uint8_t *)&mac_addr0; 2078303628Ssbruno data->hw_addr[0] = hw_addr[3]; 2079303628Ssbruno data->hw_addr[1] = hw_addr[2]; 2080303628Ssbruno data->hw_addr[2] = hw_addr[1]; 2081303628Ssbruno data->hw_addr[3] = hw_addr[0]; 2082303628Ssbruno 2083303628Ssbruno hw_addr = (const uint8_t *)&mac_addr1; 2084303628Ssbruno data->hw_addr[4] = hw_addr[1]; 2085303628Ssbruno data->hw_addr[5] = hw_addr[0]; 2086303628Ssbruno 2087303628Ssbruno return; 2088303628Ssbruno } 2089303628Ssbruno 2090303628Ssbruno device_printf(sc->sc_dev, "%s: mac address not found\n", __func__); 2091303628Ssbruno memset(data->hw_addr, 0, sizeof(data->hw_addr)); 2092303628Ssbruno} 2093303628Ssbruno 2094286441Srpaulostatic int 2095303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2096303628Ssbruno const uint16_t *phy_sku) 2097303628Ssbruno{ 2098330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2099303628Ssbruno return le16_to_cpup(nvm_sw + IWM_SKU); 2100303628Ssbruno 2101303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000)); 2102303628Ssbruno} 2103303628Ssbruno 2104303628Ssbrunostatic int 2105303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2106303628Ssbruno{ 2107330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2108303628Ssbruno return le16_to_cpup(nvm_sw + IWM_NVM_VERSION); 2109303628Ssbruno else 2110303628Ssbruno return le32_to_cpup((const uint32_t *)(nvm_sw + 2111303628Ssbruno IWM_NVM_VERSION_8000)); 2112303628Ssbruno} 2113303628Ssbruno 2114303628Ssbrunostatic int 2115303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2116303628Ssbruno const uint16_t *phy_sku) 2117303628Ssbruno{ 2118330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2119303628Ssbruno return le16_to_cpup(nvm_sw + IWM_RADIO_CFG); 2120303628Ssbruno 2121303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); 2122303628Ssbruno} 2123303628Ssbruno 2124303628Ssbrunostatic int 2125303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2126303628Ssbruno{ 2127303628Ssbruno int n_hw_addr; 2128303628Ssbruno 2129330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2130303628Ssbruno return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); 2131303628Ssbruno 2132303628Ssbruno n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000)); 2133303628Ssbruno 2134303628Ssbruno return n_hw_addr & IWM_N_HW_ADDR_MASK; 2135303628Ssbruno} 2136303628Ssbruno 2137303628Ssbrunostatic void 2138303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data, 2139303628Ssbruno uint32_t radio_cfg) 2140303628Ssbruno{ 2141330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2142303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); 2143303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); 2144303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); 2145303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); 2146303628Ssbruno return; 2147303628Ssbruno } 2148303628Ssbruno 2149303628Ssbruno /* set the radio configuration for family 8000 */ 2150303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg); 2151303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg); 2152303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg); 2153303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg); 2154303628Ssbruno data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg); 2155303628Ssbruno data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg); 2156303628Ssbruno} 2157303628Ssbruno 2158303628Ssbrunostatic int 2159330165Seadleriwm_set_hw_address(struct iwm_softc *sc, struct iwm_nvm_data *data, 2160330165Seadler const uint16_t *nvm_hw, const uint16_t *mac_override) 2161330165Seadler{ 2162330165Seadler#ifdef notyet /* for FAMILY 9000 */ 2163330165Seadler if (cfg->mac_addr_from_csr) { 2164330165Seadler iwm_set_hw_address_from_csr(sc, data); 2165330165Seadler } else 2166330165Seadler#endif 2167330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2168330165Seadler const uint8_t *hw_addr = (const uint8_t *)(nvm_hw + IWM_HW_ADDR); 2169330165Seadler 2170330165Seadler /* The byte order is little endian 16 bit, meaning 214365 */ 2171330165Seadler data->hw_addr[0] = hw_addr[1]; 2172330165Seadler data->hw_addr[1] = hw_addr[0]; 2173330165Seadler data->hw_addr[2] = hw_addr[3]; 2174330165Seadler data->hw_addr[3] = hw_addr[2]; 2175330165Seadler data->hw_addr[4] = hw_addr[5]; 2176330165Seadler data->hw_addr[5] = hw_addr[4]; 2177330165Seadler } else { 2178330165Seadler iwm_set_hw_address_family_8000(sc, data, mac_override, nvm_hw); 2179330165Seadler } 2180330165Seadler 2181330165Seadler if (!iwm_is_valid_ether_addr(data->hw_addr)) { 2182330165Seadler device_printf(sc->sc_dev, "no valid mac address was found\n"); 2183330165Seadler return EINVAL; 2184330165Seadler } 2185330165Seadler 2186330165Seadler return 0; 2187330165Seadler} 2188330165Seadler 2189330165Seadlerstatic struct iwm_nvm_data * 2190286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc, 2191303628Ssbruno const uint16_t *nvm_hw, const uint16_t *nvm_sw, 2192303628Ssbruno const uint16_t *nvm_calib, const uint16_t *mac_override, 2193303628Ssbruno const uint16_t *phy_sku, const uint16_t *regulatory) 2194286441Srpaulo{ 2195330165Seadler struct iwm_nvm_data *data; 2196303628Ssbruno uint32_t sku, radio_cfg; 2197330217Seadler uint16_t lar_config; 2198286441Srpaulo 2199330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2200330165Seadler data = malloc(sizeof(*data) + 2201330165Seadler IWM_NUM_CHANNELS * sizeof(uint16_t), 2202330165Seadler M_DEVBUF, M_NOWAIT | M_ZERO); 2203330165Seadler } else { 2204330165Seadler data = malloc(sizeof(*data) + 2205330165Seadler IWM_NUM_CHANNELS_8000 * sizeof(uint16_t), 2206330165Seadler M_DEVBUF, M_NOWAIT | M_ZERO); 2207330165Seadler } 2208330165Seadler if (!data) 2209330165Seadler return NULL; 2210330165Seadler 2211303628Ssbruno data->nvm_version = iwm_get_nvm_version(sc, nvm_sw); 2212286441Srpaulo 2213303628Ssbruno radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku); 2214303628Ssbruno iwm_set_radio_cfg(sc, data, radio_cfg); 2215286441Srpaulo 2216303628Ssbruno sku = iwm_get_sku(sc, nvm_sw, phy_sku); 2217286441Srpaulo data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; 2218286441Srpaulo data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; 2219286441Srpaulo data->sku_cap_11n_enable = 0; 2220286441Srpaulo 2221303628Ssbruno data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw); 2222286441Srpaulo 2223330217Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 2224330217Seadler uint16_t lar_offset = data->nvm_version < 0xE39 ? 2225330217Seadler IWM_NVM_LAR_OFFSET_8000_OLD : 2226330217Seadler IWM_NVM_LAR_OFFSET_8000; 2227330217Seadler 2228330217Seadler lar_config = le16_to_cpup(regulatory + lar_offset); 2229330217Seadler data->lar_enabled = !!(lar_config & 2230330217Seadler IWM_NVM_LAR_ENABLED_8000); 2231330217Seadler } 2232330217Seadler 2233330165Seadler /* If no valid mac address was found - bail out */ 2234330165Seadler if (iwm_set_hw_address(sc, data, nvm_hw, mac_override)) { 2235330165Seadler free(data, M_DEVBUF); 2236330165Seadler return NULL; 2237303628Ssbruno } 2238286441Srpaulo 2239330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 2240303628Ssbruno memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS], 2241303628Ssbruno IWM_NUM_CHANNELS * sizeof(uint16_t)); 2242303628Ssbruno } else { 2243303628Ssbruno memcpy(data->nvm_ch_flags, ®ulatory[IWM_NVM_CHANNELS_8000], 2244303628Ssbruno IWM_NUM_CHANNELS_8000 * sizeof(uint16_t)); 2245303628Ssbruno } 2246286441Srpaulo 2247330165Seadler return data; 2248286441Srpaulo} 2249286441Srpaulo 2250330165Seadlerstatic void 2251330165Seadleriwm_free_nvm_data(struct iwm_nvm_data *data) 2252330165Seadler{ 2253330165Seadler if (data != NULL) 2254330165Seadler free(data, M_DEVBUF); 2255330165Seadler} 2256286441Srpaulo 2257330165Seadlerstatic struct iwm_nvm_data * 2258286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) 2259286441Srpaulo{ 2260303628Ssbruno const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; 2261286441Srpaulo 2262286441Srpaulo /* Checking for required sections */ 2263330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 2264303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2265330166Seadler !sections[sc->cfg->nvm_hw_section_num].data) { 2266303628Ssbruno device_printf(sc->sc_dev, 2267303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2268330165Seadler return NULL; 2269303628Ssbruno } 2270330166Seadler } else if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 2271303628Ssbruno /* SW and REGULATORY sections are mandatory */ 2272303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2273303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) { 2274303628Ssbruno device_printf(sc->sc_dev, 2275303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2276330165Seadler return NULL; 2277303628Ssbruno } 2278303628Ssbruno /* MAC_OVERRIDE or at least HW section must exist */ 2279330166Seadler if (!sections[sc->cfg->nvm_hw_section_num].data && 2280303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) { 2281303628Ssbruno device_printf(sc->sc_dev, 2282303628Ssbruno "Can't parse mac_address, empty sections\n"); 2283330165Seadler return NULL; 2284303628Ssbruno } 2285303628Ssbruno 2286303628Ssbruno /* PHY_SKU section is mandatory in B0 */ 2287303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) { 2288303628Ssbruno device_printf(sc->sc_dev, 2289303628Ssbruno "Can't parse phy_sku in B0, empty sections\n"); 2290330165Seadler return NULL; 2291303628Ssbruno } 2292303628Ssbruno } else { 2293330166Seadler panic("unknown device family %d\n", sc->cfg->device_family); 2294286441Srpaulo } 2295286441Srpaulo 2296330166Seadler hw = (const uint16_t *) sections[sc->cfg->nvm_hw_section_num].data; 2297286441Srpaulo sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; 2298303628Ssbruno calib = (const uint16_t *) 2299303628Ssbruno sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; 2300303628Ssbruno regulatory = (const uint16_t *) 2301303628Ssbruno sections[IWM_NVM_SECTION_TYPE_REGULATORY].data; 2302303628Ssbruno mac_override = (const uint16_t *) 2303303628Ssbruno sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data; 2304303628Ssbruno phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data; 2305303628Ssbruno 2306303628Ssbruno return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override, 2307303628Ssbruno phy_sku, regulatory); 2308286441Srpaulo} 2309286441Srpaulo 2310286441Srpaulostatic int 2311286441Srpauloiwm_nvm_init(struct iwm_softc *sc) 2312286441Srpaulo{ 2313330165Seadler struct iwm_nvm_section nvm_sections[IWM_NVM_MAX_NUM_SECTIONS]; 2314330165Seadler int i, ret, section; 2315330165Seadler uint32_t size_read = 0; 2316330165Seadler uint8_t *nvm_buffer, *temp; 2317286441Srpaulo uint16_t len; 2318286441Srpaulo 2319330165Seadler memset(nvm_sections, 0, sizeof(nvm_sections)); 2320286441Srpaulo 2321330166Seadler if (sc->cfg->nvm_hw_section_num >= IWM_NVM_MAX_NUM_SECTIONS) 2322330165Seadler return EINVAL; 2323301970Sadrian 2324330165Seadler /* load NVM values from nic */ 2325330165Seadler /* Read From FW NVM */ 2326330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Read from NVM\n"); 2327286441Srpaulo 2328330166Seadler nvm_buffer = malloc(sc->cfg->eeprom_size, M_DEVBUF, M_NOWAIT | M_ZERO); 2329330165Seadler if (!nvm_buffer) 2330330165Seadler return ENOMEM; 2331330165Seadler for (section = 0; section < IWM_NVM_MAX_NUM_SECTIONS; section++) { 2332330165Seadler /* we override the constness for initial read */ 2333330165Seadler ret = iwm_nvm_read_section(sc, section, nvm_buffer, 2334330165Seadler &len, size_read); 2335330165Seadler if (ret) 2336303628Ssbruno continue; 2337330165Seadler size_read += len; 2338330165Seadler temp = malloc(len, M_DEVBUF, M_NOWAIT); 2339330165Seadler if (!temp) { 2340330165Seadler ret = ENOMEM; 2341286441Srpaulo break; 2342286441Srpaulo } 2343330165Seadler memcpy(temp, nvm_buffer, len); 2344330165Seadler 2345330165Seadler nvm_sections[section].data = temp; 2346286441Srpaulo nvm_sections[section].length = len; 2347286441Srpaulo } 2348330165Seadler if (!size_read) 2349330165Seadler device_printf(sc->sc_dev, "OTP is blank\n"); 2350330165Seadler free(nvm_buffer, M_DEVBUF); 2351286441Srpaulo 2352330165Seadler sc->nvm_data = iwm_parse_nvm_sections(sc, nvm_sections); 2353330165Seadler if (!sc->nvm_data) 2354330165Seadler return EINVAL; 2355330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 2356330165Seadler "nvm version = %x\n", sc->nvm_data->nvm_version); 2357330165Seadler 2358330165Seadler for (i = 0; i < IWM_NVM_MAX_NUM_SECTIONS; i++) { 2359301970Sadrian if (nvm_sections[i].data != NULL) 2360301970Sadrian free(nvm_sections[i].data, M_DEVBUF); 2361301970Sadrian } 2362301970Sadrian 2363330165Seadler return 0; 2364286441Srpaulo} 2365286441Srpaulo 2366286441Srpaulostatic int 2367330182Seadleriwm_pcie_load_section(struct iwm_softc *sc, uint8_t section_num, 2368330182Seadler const struct iwm_fw_desc *section) 2369286441Srpaulo{ 2370330182Seadler struct iwm_dma_info *dma = &sc->fw_dma; 2371330182Seadler uint8_t *v_addr; 2372330182Seadler bus_addr_t p_addr; 2373330182Seadler uint32_t offset, chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, section->len); 2374330182Seadler int ret = 0; 2375303628Ssbruno 2376330182Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2377330182Seadler "%s: [%d] uCode section being loaded...\n", 2378330182Seadler __func__, section_num); 2379303628Ssbruno 2380330182Seadler v_addr = dma->vaddr; 2381330182Seadler p_addr = dma->paddr; 2382303628Ssbruno 2383330182Seadler for (offset = 0; offset < section->len; offset += chunk_sz) { 2384330182Seadler uint32_t copy_size, dst_addr; 2385330182Seadler int extended_addr = FALSE; 2386303628Ssbruno 2387330182Seadler copy_size = MIN(chunk_sz, section->len - offset); 2388330182Seadler dst_addr = section->offset + offset; 2389330182Seadler 2390330182Seadler if (dst_addr >= IWM_FW_MEM_EXTENDED_START && 2391330182Seadler dst_addr <= IWM_FW_MEM_EXTENDED_END) 2392330182Seadler extended_addr = TRUE; 2393330182Seadler 2394330182Seadler if (extended_addr) 2395330182Seadler iwm_set_bits_prph(sc, IWM_LMPM_CHICK, 2396330182Seadler IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2397330182Seadler 2398330182Seadler memcpy(v_addr, (const uint8_t *)section->data + offset, 2399330182Seadler copy_size); 2400330182Seadler bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 2401330182Seadler ret = iwm_pcie_load_firmware_chunk(sc, dst_addr, p_addr, 2402330182Seadler copy_size); 2403330182Seadler 2404330182Seadler if (extended_addr) 2405330182Seadler iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, 2406330182Seadler IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2407330182Seadler 2408330182Seadler if (ret) { 2409330182Seadler device_printf(sc->sc_dev, 2410330182Seadler "%s: Could not load the [%d] uCode section\n", 2411330182Seadler __func__, section_num); 2412303628Ssbruno break; 2413330182Seadler } 2414303628Ssbruno } 2415303628Ssbruno 2416330182Seadler return ret; 2417303628Ssbruno} 2418303628Ssbruno 2419330182Seadler/* 2420330182Seadler * ucode 2421330182Seadler */ 2422303628Ssbrunostatic int 2423330182Seadleriwm_pcie_load_firmware_chunk(struct iwm_softc *sc, uint32_t dst_addr, 2424330182Seadler bus_addr_t phy_addr, uint32_t byte_cnt) 2425303628Ssbruno{ 2426330182Seadler int ret; 2427286441Srpaulo 2428303628Ssbruno sc->sc_fw_chunk_done = 0; 2429303628Ssbruno 2430286441Srpaulo if (!iwm_nic_lock(sc)) 2431286441Srpaulo return EBUSY; 2432286441Srpaulo 2433286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2434286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 2435330182Seadler 2436286441Srpaulo IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), 2437286441Srpaulo dst_addr); 2438330182Seadler 2439286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), 2440330182Seadler phy_addr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 2441330182Seadler 2442286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), 2443330182Seadler (iwm_get_dma_hi_addr(phy_addr) 2444330182Seadler << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 2445330182Seadler 2446286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 2447286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 2448286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | 2449286441Srpaulo IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 2450330182Seadler 2451286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2452286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 2453286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 2454286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 2455286441Srpaulo 2456286441Srpaulo iwm_nic_unlock(sc); 2457286441Srpaulo 2458330182Seadler /* wait up to 5s for this segment to load */ 2459330182Seadler ret = 0; 2460330182Seadler while (!sc->sc_fw_chunk_done) { 2461330182Seadler ret = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz); 2462330182Seadler if (ret) 2463286441Srpaulo break; 2464330182Seadler } 2465286441Srpaulo 2466330182Seadler if (ret != 0) { 2467303628Ssbruno device_printf(sc->sc_dev, 2468303628Ssbruno "fw chunk addr 0x%x len %d failed to load\n", 2469303628Ssbruno dst_addr, byte_cnt); 2470330182Seadler return ETIMEDOUT; 2471303628Ssbruno } 2472303628Ssbruno 2473330182Seadler return 0; 2474286441Srpaulo} 2475286441Srpaulo 2476330182Seadlerstatic int 2477330182Seadleriwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, 2478330182Seadler const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) 2479303628Ssbruno{ 2480303628Ssbruno int shift_param; 2481330182Seadler int i, ret = 0, sec_num = 0x1; 2482303628Ssbruno uint32_t val, last_read_idx = 0; 2483303628Ssbruno 2484303628Ssbruno if (cpu == 1) { 2485303628Ssbruno shift_param = 0; 2486303628Ssbruno *first_ucode_section = 0; 2487303628Ssbruno } else { 2488303628Ssbruno shift_param = 16; 2489303628Ssbruno (*first_ucode_section)++; 2490303628Ssbruno } 2491303628Ssbruno 2492330168Seadler for (i = *first_ucode_section; i < IWM_UCODE_SECTION_MAX; i++) { 2493303628Ssbruno last_read_idx = i; 2494303628Ssbruno 2495303628Ssbruno /* 2496303628Ssbruno * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 2497303628Ssbruno * CPU1 to CPU2. 2498303628Ssbruno * PAGING_SEPARATOR_SECTION delimiter - separate between 2499303628Ssbruno * CPU2 non paged to CPU2 paging sec. 2500303628Ssbruno */ 2501330182Seadler if (!image->fw_sect[i].data || 2502330182Seadler image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || 2503330182Seadler image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { 2504330182Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2505330182Seadler "Break since Data not valid or Empty section, sec = %d\n", 2506330182Seadler i); 2507303628Ssbruno break; 2508303628Ssbruno } 2509330182Seadler ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); 2510330182Seadler if (ret) 2511330182Seadler return ret; 2512303628Ssbruno 2513303628Ssbruno /* Notify the ucode of the loaded section number and status */ 2514303628Ssbruno if (iwm_nic_lock(sc)) { 2515303628Ssbruno val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS); 2516303628Ssbruno val = val | (sec_num << shift_param); 2517303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val); 2518303628Ssbruno sec_num = (sec_num << 1) | 0x1; 2519303628Ssbruno iwm_nic_unlock(sc); 2520303628Ssbruno } 2521303628Ssbruno } 2522303628Ssbruno 2523303628Ssbruno *first_ucode_section = last_read_idx; 2524303628Ssbruno 2525330183Seadler iwm_enable_interrupts(sc); 2526330183Seadler 2527303628Ssbruno if (iwm_nic_lock(sc)) { 2528303628Ssbruno if (cpu == 1) 2529303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); 2530303628Ssbruno else 2531303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); 2532303628Ssbruno iwm_nic_unlock(sc); 2533303628Ssbruno } 2534303628Ssbruno 2535303628Ssbruno return 0; 2536303628Ssbruno} 2537303628Ssbruno 2538330182Seadlerstatic int 2539330182Seadleriwm_pcie_load_cpu_sections(struct iwm_softc *sc, 2540330182Seadler const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) 2541303628Ssbruno{ 2542330182Seadler int shift_param; 2543330182Seadler int i, ret = 0; 2544330182Seadler uint32_t last_read_idx = 0; 2545303628Ssbruno 2546330182Seadler if (cpu == 1) { 2547330182Seadler shift_param = 0; 2548330182Seadler *first_ucode_section = 0; 2549330182Seadler } else { 2550330182Seadler shift_param = 16; 2551330182Seadler (*first_ucode_section)++; 2552330182Seadler } 2553303628Ssbruno 2554330182Seadler for (i = *first_ucode_section; i < IWM_UCODE_SECTION_MAX; i++) { 2555330182Seadler last_read_idx = i; 2556303628Ssbruno 2557330182Seadler /* 2558330182Seadler * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 2559330182Seadler * CPU1 to CPU2. 2560330182Seadler * PAGING_SEPARATOR_SECTION delimiter - separate between 2561330182Seadler * CPU2 non paged to CPU2 paging sec. 2562330182Seadler */ 2563330182Seadler if (!image->fw_sect[i].data || 2564330182Seadler image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || 2565330182Seadler image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { 2566330182Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2567330182Seadler "Break since Data not valid or Empty section, sec = %d\n", 2568330182Seadler i); 2569330182Seadler break; 2570330182Seadler } 2571303628Ssbruno 2572330182Seadler ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); 2573330182Seadler if (ret) 2574330182Seadler return ret; 2575330182Seadler } 2576303628Ssbruno 2577330182Seadler *first_ucode_section = last_read_idx; 2578330182Seadler 2579330182Seadler return 0; 2580330182Seadler 2581303628Ssbruno} 2582303628Ssbruno 2583286441Srpaulostatic int 2584330182Seadleriwm_pcie_load_given_ucode(struct iwm_softc *sc, 2585330182Seadler const struct iwm_fw_sects *image) 2586286441Srpaulo{ 2587330182Seadler int ret = 0; 2588330182Seadler int first_ucode_section; 2589286441Srpaulo 2590330182Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n", 2591330182Seadler image->is_dual_cpus ? "Dual" : "Single"); 2592286441Srpaulo 2593330182Seadler /* load to FW the binary non secured sections of CPU1 */ 2594330182Seadler ret = iwm_pcie_load_cpu_sections(sc, image, 1, &first_ucode_section); 2595330182Seadler if (ret) 2596330182Seadler return ret; 2597330182Seadler 2598330182Seadler if (image->is_dual_cpus) { 2599330182Seadler /* set CPU2 header address */ 2600330199Seadler if (iwm_nic_lock(sc)) { 2601330199Seadler iwm_write_prph(sc, 2602330199Seadler IWM_LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR, 2603330199Seadler IWM_LMPM_SECURE_CPU2_HDR_MEM_SPACE); 2604330200Seadler iwm_nic_unlock(sc); 2605330199Seadler } 2606330182Seadler 2607330182Seadler /* load to FW the binary sections of CPU2 */ 2608330182Seadler ret = iwm_pcie_load_cpu_sections(sc, image, 2, 2609330182Seadler &first_ucode_section); 2610330182Seadler if (ret) 2611330182Seadler return ret; 2612286441Srpaulo } 2613286441Srpaulo 2614330183Seadler iwm_enable_interrupts(sc); 2615330183Seadler 2616330183Seadler /* release CPU reset */ 2617286441Srpaulo IWM_WRITE(sc, IWM_CSR_RESET, 0); 2618286441Srpaulo 2619303628Ssbruno return 0; 2620303628Ssbruno} 2621303628Ssbruno 2622330182Seadlerint 2623330182Seadleriwm_pcie_load_given_ucode_8000(struct iwm_softc *sc, 2624330182Seadler const struct iwm_fw_sects *image) 2625330182Seadler{ 2626330182Seadler int ret = 0; 2627330182Seadler int first_ucode_section; 2628330182Seadler 2629330182Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, "working with %s CPU\n", 2630330182Seadler image->is_dual_cpus ? "Dual" : "Single"); 2631330182Seadler 2632330182Seadler /* configure the ucode to be ready to get the secured image */ 2633330182Seadler /* release CPU reset */ 2634330199Seadler if (iwm_nic_lock(sc)) { 2635330199Seadler iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, 2636330199Seadler IWM_RELEASE_CPU_RESET_BIT); 2637330200Seadler iwm_nic_unlock(sc); 2638330199Seadler } 2639330182Seadler 2640330182Seadler /* load to FW the binary Secured sections of CPU1 */ 2641330182Seadler ret = iwm_pcie_load_cpu_sections_8000(sc, image, 1, 2642330182Seadler &first_ucode_section); 2643330182Seadler if (ret) 2644330182Seadler return ret; 2645330182Seadler 2646330182Seadler /* load to FW the binary sections of CPU2 */ 2647330182Seadler return iwm_pcie_load_cpu_sections_8000(sc, image, 2, 2648330182Seadler &first_ucode_section); 2649330182Seadler} 2650330182Seadler 2651330183Seadler/* XXX Get rid of this definition */ 2652330183Seadlerstatic inline void 2653330183Seadleriwm_enable_fw_load_int(struct iwm_softc *sc) 2654303628Ssbruno{ 2655330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_INTR, "Enabling FW load interrupt\n"); 2656330183Seadler sc->sc_intmask = IWM_CSR_INT_BIT_FH_TX; 2657330183Seadler IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 2658286441Srpaulo} 2659286441Srpaulo 2660330183Seadler/* XXX Add proper rfkill support code */ 2661286441Srpaulostatic int 2662330183Seadleriwm_start_fw(struct iwm_softc *sc, 2663330183Seadler const struct iwm_fw_sects *fw) 2664286441Srpaulo{ 2665330183Seadler int ret; 2666286441Srpaulo 2667330183Seadler /* This may fail if AMT took ownership of the device */ 2668330183Seadler if (iwm_prepare_card_hw(sc)) { 2669330183Seadler device_printf(sc->sc_dev, 2670330183Seadler "%s: Exit HW not ready\n", __func__); 2671330183Seadler ret = EIO; 2672330183Seadler goto out; 2673286441Srpaulo } 2674286441Srpaulo 2675330183Seadler IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF); 2676330183Seadler 2677330183Seadler iwm_disable_interrupts(sc); 2678330183Seadler 2679286441Srpaulo /* make sure rfkill handshake bits are cleared */ 2680286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2681286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, 2682286441Srpaulo IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 2683286441Srpaulo 2684286441Srpaulo /* clear (again), then enable host interrupts */ 2685330183Seadler IWM_WRITE(sc, IWM_CSR_INT, 0xFFFFFFFF); 2686286441Srpaulo 2687330183Seadler ret = iwm_nic_init(sc); 2688330183Seadler if (ret) { 2689330183Seadler device_printf(sc->sc_dev, "%s: Unable to init nic\n", __func__); 2690330183Seadler goto out; 2691330183Seadler } 2692330183Seadler 2693330183Seadler /* 2694330183Seadler * Now, we load the firmware and don't want to be interrupted, even 2695330183Seadler * by the RF-Kill interrupt (hence mask all the interrupt besides the 2696330183Seadler * FH_TX interrupt which is needed to load the firmware). If the 2697330183Seadler * RF-Kill switch is toggled, we will find out after having loaded 2698330183Seadler * the firmware and return the proper value to the caller. 2699330183Seadler */ 2700330183Seadler iwm_enable_fw_load_int(sc); 2701330183Seadler 2702286441Srpaulo /* really make sure rfkill handshake bits are cleared */ 2703286441Srpaulo /* maybe we should write a few times more? just to make sure */ 2704286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2705286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2706286441Srpaulo 2707286441Srpaulo /* Load the given image to the HW */ 2708330183Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 2709330183Seadler ret = iwm_pcie_load_given_ucode_8000(sc, fw); 2710330183Seadler else 2711330183Seadler ret = iwm_pcie_load_given_ucode(sc, fw); 2712330183Seadler 2713330183Seadler /* XXX re-check RF-Kill state */ 2714330183Seadler 2715330183Seadlerout: 2716330183Seadler return ret; 2717286441Srpaulo} 2718286441Srpaulo 2719286441Srpaulostatic int 2720286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) 2721286441Srpaulo{ 2722286441Srpaulo struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { 2723286441Srpaulo .valid = htole32(valid_tx_ant), 2724286441Srpaulo }; 2725286441Srpaulo 2726286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, 2727286441Srpaulo IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); 2728286441Srpaulo} 2729286441Srpaulo 2730286441Srpaulo/* iwlwifi: mvm/fw.c */ 2731286441Srpaulostatic int 2732286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc) 2733286441Srpaulo{ 2734286441Srpaulo struct iwm_phy_cfg_cmd phy_cfg_cmd; 2735330183Seadler enum iwm_ucode_type ucode_type = sc->cur_ucode; 2736286441Srpaulo 2737286441Srpaulo /* Set parameters */ 2738330167Seadler phy_cfg_cmd.phy_cfg = htole32(iwm_mvm_get_phy_config(sc)); 2739286441Srpaulo phy_cfg_cmd.calib_control.event_trigger = 2740286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger; 2741286441Srpaulo phy_cfg_cmd.calib_control.flow_trigger = 2742286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger; 2743286441Srpaulo 2744286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 2745286441Srpaulo "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); 2746286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, 2747286441Srpaulo sizeof(phy_cfg_cmd), &phy_cfg_cmd); 2748286441Srpaulo} 2749286441Srpaulo 2750286441Srpaulostatic int 2751330183Seadleriwm_alive_fn(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data) 2752330183Seadler{ 2753330183Seadler struct iwm_mvm_alive_data *alive_data = data; 2754330183Seadler struct iwm_mvm_alive_resp_ver1 *palive1; 2755330183Seadler struct iwm_mvm_alive_resp_ver2 *palive2; 2756330183Seadler struct iwm_mvm_alive_resp *palive; 2757330183Seadler 2758330183Seadler if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive1)) { 2759330183Seadler palive1 = (void *)pkt->data; 2760330183Seadler 2761330183Seadler sc->support_umac_log = FALSE; 2762330183Seadler sc->error_event_table = 2763330183Seadler le32toh(palive1->error_event_table_ptr); 2764330183Seadler sc->log_event_table = 2765330183Seadler le32toh(palive1->log_event_table_ptr); 2766330183Seadler alive_data->scd_base_addr = le32toh(palive1->scd_base_ptr); 2767330183Seadler 2768330183Seadler alive_data->valid = le16toh(palive1->status) == 2769330183Seadler IWM_ALIVE_STATUS_OK; 2770330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2771330183Seadler "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", 2772330183Seadler le16toh(palive1->status), palive1->ver_type, 2773330183Seadler palive1->ver_subtype, palive1->flags); 2774330183Seadler } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive2)) { 2775330183Seadler palive2 = (void *)pkt->data; 2776330183Seadler sc->error_event_table = 2777330183Seadler le32toh(palive2->error_event_table_ptr); 2778330183Seadler sc->log_event_table = 2779330183Seadler le32toh(palive2->log_event_table_ptr); 2780330183Seadler alive_data->scd_base_addr = le32toh(palive2->scd_base_ptr); 2781330183Seadler sc->umac_error_event_table = 2782330183Seadler le32toh(palive2->error_info_addr); 2783330183Seadler 2784330183Seadler alive_data->valid = le16toh(palive2->status) == 2785330183Seadler IWM_ALIVE_STATUS_OK; 2786330183Seadler if (sc->umac_error_event_table) 2787330183Seadler sc->support_umac_log = TRUE; 2788330183Seadler 2789330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2790330183Seadler "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", 2791330183Seadler le16toh(palive2->status), palive2->ver_type, 2792330183Seadler palive2->ver_subtype, palive2->flags); 2793330183Seadler 2794330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2795330183Seadler "UMAC version: Major - 0x%x, Minor - 0x%x\n", 2796330183Seadler palive2->umac_major, palive2->umac_minor); 2797330183Seadler } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive)) { 2798330183Seadler palive = (void *)pkt->data; 2799330183Seadler 2800330183Seadler sc->error_event_table = 2801330183Seadler le32toh(palive->error_event_table_ptr); 2802330183Seadler sc->log_event_table = 2803330183Seadler le32toh(palive->log_event_table_ptr); 2804330183Seadler alive_data->scd_base_addr = le32toh(palive->scd_base_ptr); 2805330183Seadler sc->umac_error_event_table = 2806330183Seadler le32toh(palive->error_info_addr); 2807330183Seadler 2808330183Seadler alive_data->valid = le16toh(palive->status) == 2809330183Seadler IWM_ALIVE_STATUS_OK; 2810330183Seadler if (sc->umac_error_event_table) 2811330183Seadler sc->support_umac_log = TRUE; 2812330183Seadler 2813330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2814330183Seadler "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", 2815330183Seadler le16toh(palive->status), palive->ver_type, 2816330183Seadler palive->ver_subtype, palive->flags); 2817330183Seadler 2818330183Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2819330183Seadler "UMAC version: Major - 0x%x, Minor - 0x%x\n", 2820330183Seadler le32toh(palive->umac_major), 2821330183Seadler le32toh(palive->umac_minor)); 2822330183Seadler } 2823330183Seadler 2824330183Seadler return TRUE; 2825330183Seadler} 2826330183Seadler 2827330183Seadlerstatic int 2828330171Seadleriwm_wait_phy_db_entry(struct iwm_softc *sc, 2829330171Seadler struct iwm_rx_packet *pkt, void *data) 2830330171Seadler{ 2831330171Seadler struct iwm_phy_db *phy_db = data; 2832330171Seadler 2833330171Seadler if (pkt->hdr.code != IWM_CALIB_RES_NOTIF_PHY_DB) { 2834330171Seadler if(pkt->hdr.code != IWM_INIT_COMPLETE_NOTIF) { 2835330171Seadler device_printf(sc->sc_dev, "%s: Unexpected cmd: %d\n", 2836330171Seadler __func__, pkt->hdr.code); 2837330171Seadler } 2838330171Seadler return TRUE; 2839330171Seadler } 2840330171Seadler 2841330171Seadler if (iwm_phy_db_set_section(phy_db, pkt)) { 2842330171Seadler device_printf(sc->sc_dev, 2843330171Seadler "%s: iwm_phy_db_set_section failed\n", __func__); 2844330171Seadler } 2845330171Seadler 2846330171Seadler return FALSE; 2847330171Seadler} 2848330171Seadler 2849330171Seadlerstatic int 2850286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, 2851286441Srpaulo enum iwm_ucode_type ucode_type) 2852286441Srpaulo{ 2853330183Seadler struct iwm_notification_wait alive_wait; 2854330183Seadler struct iwm_mvm_alive_data alive_data; 2855330183Seadler const struct iwm_fw_sects *fw; 2856330183Seadler enum iwm_ucode_type old_type = sc->cur_ucode; 2857286441Srpaulo int error; 2858330183Seadler static const uint16_t alive_cmd[] = { IWM_MVM_ALIVE }; 2859286441Srpaulo 2860303628Ssbruno if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { 2861303628Ssbruno device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", 2862303628Ssbruno error); 2863286441Srpaulo return error; 2864303628Ssbruno } 2865330183Seadler fw = &sc->sc_fw.fw_sects[ucode_type]; 2866330183Seadler sc->cur_ucode = ucode_type; 2867330183Seadler sc->ucode_loaded = FALSE; 2868286441Srpaulo 2869330183Seadler memset(&alive_data, 0, sizeof(alive_data)); 2870330183Seadler iwm_init_notification_wait(sc->sc_notif_wait, &alive_wait, 2871330183Seadler alive_cmd, nitems(alive_cmd), 2872330183Seadler iwm_alive_fn, &alive_data); 2873330183Seadler 2874330183Seadler error = iwm_start_fw(sc, fw); 2875286441Srpaulo if (error) { 2876303628Ssbruno device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error); 2877330183Seadler sc->cur_ucode = old_type; 2878330183Seadler iwm_remove_notification(sc->sc_notif_wait, &alive_wait); 2879286441Srpaulo return error; 2880286441Srpaulo } 2881286441Srpaulo 2882330183Seadler /* 2883330183Seadler * Some things may run in the background now, but we 2884330183Seadler * just wait for the ALIVE notification here. 2885330183Seadler */ 2886330183Seadler IWM_UNLOCK(sc); 2887330183Seadler error = iwm_wait_notification(sc->sc_notif_wait, &alive_wait, 2888330183Seadler IWM_MVM_UCODE_ALIVE_TIMEOUT); 2889330183Seadler IWM_LOCK(sc); 2890303628Ssbruno if (error) { 2891330183Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 2892330199Seadler uint32_t a = 0x5a5a5a5a, b = 0x5a5a5a5a; 2893330199Seadler if (iwm_nic_lock(sc)) { 2894330199Seadler a = iwm_read_prph(sc, IWM_SB_CPU_1_STATUS); 2895330199Seadler b = iwm_read_prph(sc, IWM_SB_CPU_2_STATUS); 2896330200Seadler iwm_nic_unlock(sc); 2897330199Seadler } 2898330183Seadler device_printf(sc->sc_dev, 2899330183Seadler "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", 2900330199Seadler a, b); 2901330183Seadler } 2902330183Seadler sc->cur_ucode = old_type; 2903330183Seadler return error; 2904303628Ssbruno } 2905330183Seadler 2906330183Seadler if (!alive_data.valid) { 2907330183Seadler device_printf(sc->sc_dev, "%s: Loaded ucode is not valid\n", 2908330183Seadler __func__); 2909330183Seadler sc->cur_ucode = old_type; 2910330183Seadler return EIO; 2911330183Seadler } 2912330183Seadler 2913330183Seadler iwm_trans_pcie_fw_alive(sc, alive_data.scd_base_addr); 2914330183Seadler 2915330183Seadler /* 2916330183Seadler * configure and operate fw paging mechanism. 2917330183Seadler * driver configures the paging flow only once, CPU2 paging image 2918330183Seadler * included in the IWM_UCODE_INIT image. 2919330183Seadler */ 2920330183Seadler if (fw->paging_mem_size) { 2921330192Seadler error = iwm_save_fw_paging(sc, fw); 2922330192Seadler if (error) { 2923330192Seadler device_printf(sc->sc_dev, 2924330192Seadler "%s: failed to save the FW paging image\n", 2925330192Seadler __func__); 2926330192Seadler return error; 2927330192Seadler } 2928330192Seadler 2929330192Seadler error = iwm_send_paging_cmd(sc, fw); 2930330192Seadler if (error) { 2931330192Seadler device_printf(sc->sc_dev, 2932330192Seadler "%s: failed to send the paging cmd\n", __func__); 2933330192Seadler iwm_free_fw_paging(sc); 2934330192Seadler return error; 2935330192Seadler } 2936330183Seadler } 2937330183Seadler 2938330183Seadler if (!error) 2939330183Seadler sc->ucode_loaded = TRUE; 2940303628Ssbruno return error; 2941286441Srpaulo} 2942286441Srpaulo 2943286441Srpaulo/* 2944286441Srpaulo * mvm misc bits 2945286441Srpaulo */ 2946286441Srpaulo 2947286441Srpaulo/* 2948286441Srpaulo * follows iwlwifi/fw.c 2949286441Srpaulo */ 2950286441Srpaulostatic int 2951286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) 2952286441Srpaulo{ 2953330171Seadler struct iwm_notification_wait calib_wait; 2954330171Seadler static const uint16_t init_complete[] = { 2955330171Seadler IWM_INIT_COMPLETE_NOTIF, 2956330171Seadler IWM_CALIB_RES_NOTIF_PHY_DB 2957330171Seadler }; 2958330171Seadler int ret; 2959286441Srpaulo 2960286441Srpaulo /* do not operate with rfkill switch turned on */ 2961286441Srpaulo if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { 2962286441Srpaulo device_printf(sc->sc_dev, 2963286441Srpaulo "radio is disabled by hardware switch\n"); 2964286441Srpaulo return EPERM; 2965286441Srpaulo } 2966286441Srpaulo 2967330171Seadler iwm_init_notification_wait(sc->sc_notif_wait, 2968330171Seadler &calib_wait, 2969330171Seadler init_complete, 2970330171Seadler nitems(init_complete), 2971330171Seadler iwm_wait_phy_db_entry, 2972330171Seadler sc->sc_phy_db); 2973330171Seadler 2974330171Seadler /* Will also start the device */ 2975330171Seadler ret = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_INIT); 2976330171Seadler if (ret) { 2977330171Seadler device_printf(sc->sc_dev, "Failed to start INIT ucode: %d\n", 2978330171Seadler ret); 2979330171Seadler goto error; 2980301192Sadrian } 2981286441Srpaulo 2982286441Srpaulo if (justnvm) { 2983330171Seadler /* Read nvm */ 2984330171Seadler ret = iwm_nvm_init(sc); 2985330171Seadler if (ret) { 2986286441Srpaulo device_printf(sc->sc_dev, "failed to read nvm\n"); 2987330171Seadler goto error; 2988286441Srpaulo } 2989330165Seadler IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->nvm_data->hw_addr); 2990330171Seadler goto error; 2991286441Srpaulo } 2992286441Srpaulo 2993330171Seadler ret = iwm_send_bt_init_conf(sc); 2994330171Seadler if (ret) { 2995303628Ssbruno device_printf(sc->sc_dev, 2996330171Seadler "failed to send bt coex configuration: %d\n", ret); 2997330171Seadler goto error; 2998303628Ssbruno } 2999303628Ssbruno 3000303628Ssbruno /* Init Smart FIFO. */ 3001330171Seadler ret = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF); 3002330171Seadler if (ret) 3003330171Seadler goto error; 3004303628Ssbruno 3005286441Srpaulo /* Send TX valid antennas before triggering calibrations */ 3006330171Seadler ret = iwm_send_tx_ant_cfg(sc, iwm_mvm_get_valid_tx_ant(sc)); 3007330171Seadler if (ret) { 3008303628Ssbruno device_printf(sc->sc_dev, 3009330171Seadler "failed to send antennas before calibration: %d\n", ret); 3010330171Seadler goto error; 3011303628Ssbruno } 3012286441Srpaulo 3013286441Srpaulo /* 3014303628Ssbruno * Send phy configurations command to init uCode 3015303628Ssbruno * to start the 16.0 uCode init image internal calibrations. 3016303628Ssbruno */ 3017330171Seadler ret = iwm_send_phy_cfg_cmd(sc); 3018330171Seadler if (ret) { 3019286441Srpaulo device_printf(sc->sc_dev, 3020330171Seadler "%s: Failed to run INIT calibrations: %d\n", 3021330171Seadler __func__, ret); 3022330171Seadler goto error; 3023286441Srpaulo } 3024286441Srpaulo 3025286441Srpaulo /* 3026286441Srpaulo * Nothing to do but wait for the init complete notification 3027330171Seadler * from the firmware. 3028286441Srpaulo */ 3029330171Seadler IWM_UNLOCK(sc); 3030330171Seadler ret = iwm_wait_notification(sc->sc_notif_wait, &calib_wait, 3031330171Seadler IWM_MVM_UCODE_CALIB_TIMEOUT); 3032330171Seadler IWM_LOCK(sc); 3033286441Srpaulo 3034303628Ssbruno 3035330171Seadler goto out; 3036330171Seadler 3037330171Seadlererror: 3038330171Seadler iwm_remove_notification(sc->sc_notif_wait, &calib_wait); 3039330171Seadlerout: 3040330171Seadler return ret; 3041286441Srpaulo} 3042286441Srpaulo 3043286441Srpaulo/* 3044286441Srpaulo * receive side 3045286441Srpaulo */ 3046286441Srpaulo 3047286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */ 3048286441Srpaulostatic int 3049286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) 3050286441Srpaulo{ 3051286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 3052286441Srpaulo struct iwm_rx_data *data = &ring->data[idx]; 3053286441Srpaulo struct mbuf *m; 3054330197Seadler bus_dmamap_t dmamap; 3055303628Ssbruno bus_dma_segment_t seg; 3056303628Ssbruno int nsegs, error; 3057286441Srpaulo 3058286441Srpaulo m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); 3059286441Srpaulo if (m == NULL) 3060286441Srpaulo return ENOBUFS; 3061286441Srpaulo 3062286441Srpaulo m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; 3063303628Ssbruno error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m, 3064303628Ssbruno &seg, &nsegs, BUS_DMA_NOWAIT); 3065303628Ssbruno if (error != 0) { 3066286441Srpaulo device_printf(sc->sc_dev, 3067301845Sadrian "%s: can't map mbuf, error %d\n", __func__, error); 3068330197Seadler m_freem(m); 3069330197Seadler return error; 3070286441Srpaulo } 3071301845Sadrian 3072301845Sadrian if (data->m != NULL) 3073301845Sadrian bus_dmamap_unload(ring->data_dmat, data->map); 3074301845Sadrian 3075301845Sadrian /* Swap ring->spare_map with data->map */ 3076301845Sadrian dmamap = data->map; 3077301845Sadrian data->map = ring->spare_map; 3078301845Sadrian ring->spare_map = dmamap; 3079301845Sadrian 3080286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD); 3081301845Sadrian data->m = m; 3082286441Srpaulo 3083286441Srpaulo /* Update RX descriptor. */ 3084303628Ssbruno KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned")); 3085303628Ssbruno ring->desc[idx] = htole32(seg.ds_addr >> 8); 3086286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3087286441Srpaulo BUS_DMASYNC_PREWRITE); 3088286441Srpaulo 3089286441Srpaulo return 0; 3090286441Srpaulo} 3091286441Srpaulo 3092286441Srpaulo/* iwlwifi: mvm/rx.c */ 3093286441Srpaulo/* 3094286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API 3095286441Srpaulo * values are reported by the fw as positive values - need to negate 3096286441Srpaulo * to obtain their dBM. Account for missing antennas by replacing 0 3097286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value. 3098286441Srpaulo */ 3099286441Srpaulostatic int 3100286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 3101286441Srpaulo{ 3102286441Srpaulo int energy_a, energy_b, energy_c, max_energy; 3103286441Srpaulo uint32_t val; 3104286441Srpaulo 3105286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); 3106286441Srpaulo energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> 3107286441Srpaulo IWM_RX_INFO_ENERGY_ANT_A_POS; 3108286441Srpaulo energy_a = energy_a ? -energy_a : -256; 3109286441Srpaulo energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> 3110286441Srpaulo IWM_RX_INFO_ENERGY_ANT_B_POS; 3111286441Srpaulo energy_b = energy_b ? -energy_b : -256; 3112286441Srpaulo energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> 3113286441Srpaulo IWM_RX_INFO_ENERGY_ANT_C_POS; 3114286441Srpaulo energy_c = energy_c ? -energy_c : -256; 3115286441Srpaulo max_energy = MAX(energy_a, energy_b); 3116286441Srpaulo max_energy = MAX(max_energy, energy_c); 3117286441Srpaulo 3118286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3119286441Srpaulo "energy In A %d B %d C %d , and max %d\n", 3120286441Srpaulo energy_a, energy_b, energy_c, max_energy); 3121286441Srpaulo 3122286441Srpaulo return max_energy; 3123286441Srpaulo} 3124286441Srpaulo 3125286441Srpaulostatic void 3126330208Seadleriwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3127286441Srpaulo{ 3128286441Srpaulo struct iwm_rx_phy_info *phy_info = (void *)pkt->data; 3129286441Srpaulo 3130286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n"); 3131286441Srpaulo 3132286441Srpaulo memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); 3133286441Srpaulo} 3134286441Srpaulo 3135286441Srpaulo/* 3136286441Srpaulo * Retrieve the average noise (in dBm) among receivers. 3137286441Srpaulo */ 3138286441Srpaulostatic int 3139330144Seadleriwm_get_noise(struct iwm_softc *sc, 3140330144Seadler const struct iwm_mvm_statistics_rx_non_phy *stats) 3141286441Srpaulo{ 3142286441Srpaulo int i, total, nbant, noise; 3143286441Srpaulo 3144286441Srpaulo total = nbant = noise = 0; 3145286441Srpaulo for (i = 0; i < 3; i++) { 3146286441Srpaulo noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; 3147330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n", 3148330144Seadler __func__, 3149330144Seadler i, 3150330144Seadler noise); 3151330144Seadler 3152286441Srpaulo if (noise) { 3153286441Srpaulo total += noise; 3154286441Srpaulo nbant++; 3155286441Srpaulo } 3156286441Srpaulo } 3157286441Srpaulo 3158330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n", 3159330144Seadler __func__, nbant, total); 3160330144Seadler#if 0 3161286441Srpaulo /* There should be at least one antenna but check anyway. */ 3162286441Srpaulo return (nbant == 0) ? -127 : (total / nbant) - 107; 3163330144Seadler#else 3164330144Seadler /* For now, just hard-code it to -96 to be safe */ 3165330144Seadler return (-96); 3166330144Seadler#endif 3167286441Srpaulo} 3168286441Srpaulo 3169330224Seadlerstatic void 3170330224Seadleriwm_mvm_handle_rx_statistics(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3171330224Seadler{ 3172330224Seadler struct iwm_notif_statistics_v10 *stats = (void *)&pkt->data; 3173330224Seadler 3174330224Seadler memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); 3175330224Seadler sc->sc_noise = iwm_get_noise(sc, &stats->rx.general); 3176330224Seadler} 3177330224Seadler 3178286441Srpaulo/* 3179286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler 3180286441Srpaulo * 3181286441Srpaulo * Handles the actual data of the Rx packet from the fw 3182286441Srpaulo */ 3183330208Seadlerstatic boolean_t 3184330208Seadleriwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, struct mbuf *m, uint32_t offset, 3185330208Seadler boolean_t stolen) 3186286441Srpaulo{ 3187287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3188286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3189286441Srpaulo struct ieee80211_frame *wh; 3190286441Srpaulo struct ieee80211_node *ni; 3191286441Srpaulo struct ieee80211_rx_stats rxs; 3192286441Srpaulo struct iwm_rx_phy_info *phy_info; 3193286441Srpaulo struct iwm_rx_mpdu_res_start *rx_res; 3194330208Seadler struct iwm_rx_packet *pkt = mtodoff(m, struct iwm_rx_packet *, offset); 3195286441Srpaulo uint32_t len; 3196286441Srpaulo uint32_t rx_pkt_status; 3197286441Srpaulo int rssi; 3198286441Srpaulo 3199286441Srpaulo phy_info = &sc->sc_last_phy_info; 3200286441Srpaulo rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; 3201286441Srpaulo wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); 3202286441Srpaulo len = le16toh(rx_res->byte_count); 3203286441Srpaulo rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); 3204286441Srpaulo 3205286441Srpaulo if (__predict_false(phy_info->cfg_phy_cnt > 20)) { 3206286441Srpaulo device_printf(sc->sc_dev, 3207286441Srpaulo "dsp size out of range [0,20]: %d\n", 3208286441Srpaulo phy_info->cfg_phy_cnt); 3209330157Seadler goto fail; 3210286441Srpaulo } 3211286441Srpaulo 3212286441Srpaulo if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || 3213286441Srpaulo !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { 3214286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3215286441Srpaulo "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); 3216330157Seadler goto fail; 3217286441Srpaulo } 3218286441Srpaulo 3219330195Seadler rssi = iwm_mvm_get_signal_strength(sc, phy_info); 3220286441Srpaulo 3221330144Seadler /* Map it to relative value */ 3222330144Seadler rssi = rssi - sc->sc_noise; 3223330144Seadler 3224286441Srpaulo /* replenish ring for the buffer we're going to feed to the sharks */ 3225330208Seadler if (!stolen && iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) { 3226286441Srpaulo device_printf(sc->sc_dev, "%s: unable to add more buffers\n", 3227286441Srpaulo __func__); 3228330157Seadler goto fail; 3229286441Srpaulo } 3230286441Srpaulo 3231330194Seadler m->m_data = pkt->data + sizeof(*rx_res); 3232330194Seadler m->m_pkthdr.len = m->m_len = len; 3233330194Seadler 3234330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3235330144Seadler "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise); 3236330144Seadler 3237286441Srpaulo ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 3238286441Srpaulo 3239286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3240286441Srpaulo "%s: phy_info: channel=%d, flags=0x%08x\n", 3241286441Srpaulo __func__, 3242286441Srpaulo le16toh(phy_info->channel), 3243286441Srpaulo le16toh(phy_info->phy_flags)); 3244286441Srpaulo 3245286441Srpaulo /* 3246286441Srpaulo * Populate an RX state struct with the provided information. 3247286441Srpaulo */ 3248286441Srpaulo bzero(&rxs, sizeof(rxs)); 3249286441Srpaulo rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; 3250286441Srpaulo rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; 3251286441Srpaulo rxs.c_ieee = le16toh(phy_info->channel); 3252286441Srpaulo if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) { 3253286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); 3254286441Srpaulo } else { 3255286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ); 3256286441Srpaulo } 3257330144Seadler 3258330144Seadler /* rssi is in 1/2db units */ 3259330576Seadler rxs.rssi = rssi * 2; 3260330576Seadler rxs.nf = sc->sc_noise; 3261286441Srpaulo 3262286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3263286441Srpaulo struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; 3264286441Srpaulo 3265286441Srpaulo tap->wr_flags = 0; 3266286441Srpaulo if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) 3267286441Srpaulo tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 3268286441Srpaulo tap->wr_chan_freq = htole16(rxs.c_freq); 3269286441Srpaulo /* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */ 3270286441Srpaulo tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); 3271286441Srpaulo tap->wr_dbm_antsignal = (int8_t)rssi; 3272286441Srpaulo tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; 3273286441Srpaulo tap->wr_tsft = phy_info->system_timestamp; 3274286441Srpaulo switch (phy_info->rate) { 3275286441Srpaulo /* CCK rates. */ 3276286441Srpaulo case 10: tap->wr_rate = 2; break; 3277286441Srpaulo case 20: tap->wr_rate = 4; break; 3278286441Srpaulo case 55: tap->wr_rate = 11; break; 3279286441Srpaulo case 110: tap->wr_rate = 22; break; 3280286441Srpaulo /* OFDM rates. */ 3281286441Srpaulo case 0xd: tap->wr_rate = 12; break; 3282286441Srpaulo case 0xf: tap->wr_rate = 18; break; 3283286441Srpaulo case 0x5: tap->wr_rate = 24; break; 3284286441Srpaulo case 0x7: tap->wr_rate = 36; break; 3285286441Srpaulo case 0x9: tap->wr_rate = 48; break; 3286286441Srpaulo case 0xb: tap->wr_rate = 72; break; 3287286441Srpaulo case 0x1: tap->wr_rate = 96; break; 3288286441Srpaulo case 0x3: tap->wr_rate = 108; break; 3289286441Srpaulo /* Unknown rate: should not happen. */ 3290286441Srpaulo default: tap->wr_rate = 0; 3291286441Srpaulo } 3292286441Srpaulo } 3293286441Srpaulo 3294286441Srpaulo IWM_UNLOCK(sc); 3295286441Srpaulo if (ni != NULL) { 3296286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m); 3297330576Seadler ieee80211_input_mimo(ni, m, &rxs); 3298286441Srpaulo ieee80211_free_node(ni); 3299286441Srpaulo } else { 3300286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m); 3301330576Seadler ieee80211_input_mimo_all(ic, m, &rxs); 3302286441Srpaulo } 3303286441Srpaulo IWM_LOCK(sc); 3304330157Seadler 3305330208Seadler return TRUE; 3306330157Seadler 3307330576Seadlerfail: counter_u64_add(ic->ic_ierrors, 1); 3308330208Seadler return FALSE; 3309286441Srpaulo} 3310286441Srpaulo 3311293100Savosstatic int 3312286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, 3313286441Srpaulo struct iwm_node *in) 3314286441Srpaulo{ 3315286441Srpaulo struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; 3316293100Savos struct ieee80211_node *ni = &in->in_ni; 3317293100Savos struct ieee80211vap *vap = ni->ni_vap; 3318286441Srpaulo int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; 3319286441Srpaulo int failack = tx_resp->failure_frame; 3320286441Srpaulo 3321286441Srpaulo KASSERT(tx_resp->frame_count == 1, ("too many frames")); 3322286441Srpaulo 3323286441Srpaulo /* Update rate control statistics. */ 3324298611Sadrian 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", 3325298611Sadrian __func__, 3326298611Sadrian (int) le16toh(tx_resp->status.status), 3327298611Sadrian (int) le16toh(tx_resp->status.sequence), 3328298611Sadrian tx_resp->frame_count, 3329298611Sadrian tx_resp->bt_kill_count, 3330298611Sadrian tx_resp->failure_rts, 3331298611Sadrian tx_resp->failure_frame, 3332298611Sadrian le32toh(tx_resp->initial_rate), 3333298611Sadrian (int) le16toh(tx_resp->wireless_media_time)); 3334298611Sadrian 3335286441Srpaulo if (status != IWM_TX_STATUS_SUCCESS && 3336286441Srpaulo status != IWM_TX_STATUS_DIRECT_DONE) { 3337293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3338286441Srpaulo IEEE80211_RATECTL_TX_FAILURE, &failack, NULL); 3339293100Savos return (1); 3340286441Srpaulo } else { 3341293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3342286441Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL); 3343293100Savos return (0); 3344286441Srpaulo } 3345286441Srpaulo} 3346286441Srpaulo 3347286441Srpaulostatic void 3348330208Seadleriwm_mvm_rx_tx_cmd(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3349286441Srpaulo{ 3350286441Srpaulo struct iwm_cmd_header *cmd_hdr = &pkt->hdr; 3351286441Srpaulo int idx = cmd_hdr->idx; 3352286441Srpaulo int qid = cmd_hdr->qid; 3353286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[qid]; 3354286441Srpaulo struct iwm_tx_data *txd = &ring->data[idx]; 3355286441Srpaulo struct iwm_node *in = txd->in; 3356293100Savos struct mbuf *m = txd->m; 3357293100Savos int status; 3358286441Srpaulo 3359293100Savos KASSERT(txd->done == 0, ("txd not done")); 3360293100Savos KASSERT(txd->in != NULL, ("txd without node")); 3361293100Savos KASSERT(txd->m != NULL, ("txd without mbuf")); 3362293100Savos 3363286441Srpaulo sc->sc_tx_timer = 0; 3364286441Srpaulo 3365293100Savos status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in); 3366286441Srpaulo 3367286441Srpaulo /* Unmap and free mbuf. */ 3368286441Srpaulo bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE); 3369286441Srpaulo bus_dmamap_unload(ring->data_dmat, txd->map); 3370286441Srpaulo 3371286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3372286441Srpaulo "free txd %p, in %p\n", txd, txd->in); 3373286441Srpaulo txd->done = 1; 3374286441Srpaulo txd->m = NULL; 3375286441Srpaulo txd->in = NULL; 3376286441Srpaulo 3377293100Savos ieee80211_tx_complete(&in->in_ni, m, status); 3378293100Savos 3379286441Srpaulo if (--ring->queued < IWM_TX_RING_LOMARK) { 3380286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 3381287197Sglebius if (sc->qfullmsk == 0) { 3382287197Sglebius iwm_start(sc); 3383286441Srpaulo } 3384286441Srpaulo } 3385286441Srpaulo} 3386286441Srpaulo 3387286441Srpaulo/* 3388286441Srpaulo * transmit side 3389286441Srpaulo */ 3390286441Srpaulo 3391286441Srpaulo/* 3392286441Srpaulo * Process a "command done" firmware notification. This is where we wakeup 3393286441Srpaulo * processes waiting for a synchronous command completion. 3394286441Srpaulo * from if_iwn 3395286441Srpaulo */ 3396286441Srpaulostatic void 3397286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3398286441Srpaulo{ 3399286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; 3400286441Srpaulo struct iwm_tx_data *data; 3401286441Srpaulo 3402286441Srpaulo if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { 3403286441Srpaulo return; /* Not a command ack. */ 3404286441Srpaulo } 3405286441Srpaulo 3406330142Seadler /* XXX wide commands? */ 3407330142Seadler IWM_DPRINTF(sc, IWM_DEBUG_CMD, 3408330142Seadler "cmd notification type 0x%x qid %d idx %d\n", 3409330142Seadler pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx); 3410330142Seadler 3411286441Srpaulo data = &ring->data[pkt->hdr.idx]; 3412286441Srpaulo 3413286441Srpaulo /* If the command was mapped in an mbuf, free it. */ 3414286441Srpaulo if (data->m != NULL) { 3415286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3416286441Srpaulo BUS_DMASYNC_POSTWRITE); 3417286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 3418286441Srpaulo m_freem(data->m); 3419286441Srpaulo data->m = NULL; 3420286441Srpaulo } 3421286441Srpaulo wakeup(&ring->desc[pkt->hdr.idx]); 3422330175Seadler 3423330175Seadler if (((pkt->hdr.idx + ring->queued) % IWM_TX_RING_COUNT) != ring->cur) { 3424330175Seadler device_printf(sc->sc_dev, 3425330175Seadler "%s: Some HCMDs skipped?: idx=%d queued=%d cur=%d\n", 3426330175Seadler __func__, pkt->hdr.idx, ring->queued, ring->cur); 3427330175Seadler /* XXX call iwm_force_nmi() */ 3428330175Seadler } 3429330175Seadler 3430330175Seadler KASSERT(ring->queued > 0, ("ring->queued is empty?")); 3431330175Seadler ring->queued--; 3432330175Seadler if (ring->queued == 0) 3433330175Seadler iwm_pcie_clear_cmd_in_flight(sc); 3434286441Srpaulo} 3435286441Srpaulo 3436286441Srpaulo#if 0 3437286441Srpaulo/* 3438286441Srpaulo * necessary only for block ack mode 3439286441Srpaulo */ 3440286441Srpaulovoid 3441286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, 3442286441Srpaulo uint16_t len) 3443286441Srpaulo{ 3444286441Srpaulo struct iwm_agn_scd_bc_tbl *scd_bc_tbl; 3445286441Srpaulo uint16_t w_val; 3446286441Srpaulo 3447286441Srpaulo scd_bc_tbl = sc->sched_dma.vaddr; 3448286441Srpaulo 3449286441Srpaulo len += 8; /* magic numbers came naturally from paris */ 3450330221Seadler len = roundup(len, 4) / 4; 3451286441Srpaulo 3452286441Srpaulo w_val = htole16(sta_id << 12 | len); 3453286441Srpaulo 3454286441Srpaulo /* Update TX scheduler. */ 3455286441Srpaulo scd_bc_tbl[qid].tfd_offset[idx] = w_val; 3456286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3457286441Srpaulo BUS_DMASYNC_PREWRITE); 3458286441Srpaulo 3459286441Srpaulo /* I really wonder what this is ?!? */ 3460286441Srpaulo if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { 3461286441Srpaulo scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; 3462286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3463286441Srpaulo BUS_DMASYNC_PREWRITE); 3464286441Srpaulo } 3465286441Srpaulo} 3466286441Srpaulo#endif 3467286441Srpaulo 3468286441Srpaulo/* 3469286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate 3470286441Srpaulo * table entry. return the index into in_ridx[]. 3471286441Srpaulo * 3472286441Srpaulo * The caller then uses that index back into in_ridx 3473286441Srpaulo * to figure out the rate index programmed /into/ 3474286441Srpaulo * the firmware for this given node. 3475286441Srpaulo */ 3476286441Srpaulostatic int 3477286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, 3478286441Srpaulo uint8_t rate) 3479286441Srpaulo{ 3480286441Srpaulo int i; 3481286441Srpaulo uint8_t r; 3482286441Srpaulo 3483286441Srpaulo for (i = 0; i < nitems(in->in_ridx); i++) { 3484286441Srpaulo r = iwm_rates[in->in_ridx[i]].rate; 3485286441Srpaulo if (rate == r) 3486286441Srpaulo return (i); 3487286441Srpaulo } 3488330156Seadler 3489330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3490330156Seadler "%s: couldn't find an entry for rate=%d\n", 3491330156Seadler __func__, 3492330156Seadler rate); 3493330156Seadler 3494286441Srpaulo /* XXX Return the first */ 3495286441Srpaulo /* XXX TODO: have it return the /lowest/ */ 3496286441Srpaulo return (0); 3497286441Srpaulo} 3498286441Srpaulo 3499330156Seadlerstatic int 3500330156Seadleriwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate) 3501330156Seadler{ 3502330156Seadler int i; 3503330156Seadler 3504330156Seadler for (i = 0; i < nitems(iwm_rates); i++) { 3505330156Seadler if (iwm_rates[i].rate == rate) 3506330156Seadler return (i); 3507330156Seadler } 3508330156Seadler /* XXX error? */ 3509330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3510330156Seadler "%s: couldn't find an entry for rate=%d\n", 3511330156Seadler __func__, 3512330156Seadler rate); 3513330156Seadler return (0); 3514330156Seadler} 3515330156Seadler 3516286441Srpaulo/* 3517298875Sadrian * Fill in the rate related information for a transmit command. 3518286441Srpaulo */ 3519286441Srpaulostatic const struct iwm_rate * 3520286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, 3521330155Seadler struct mbuf *m, struct iwm_tx_cmd *tx) 3522286441Srpaulo{ 3523286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 3524330155Seadler struct ieee80211_frame *wh; 3525330155Seadler const struct ieee80211_txparam *tp = ni->ni_txparms; 3526286441Srpaulo const struct iwm_rate *rinfo; 3527330155Seadler int type; 3528330156Seadler int ridx, rate_flags; 3529286441Srpaulo 3530330155Seadler wh = mtod(m, struct ieee80211_frame *); 3531330155Seadler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3532330155Seadler 3533286441Srpaulo tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; 3534286441Srpaulo tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; 3535286441Srpaulo 3536330155Seadler if (type == IEEE80211_FC0_TYPE_MGT) { 3537330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3538330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3539330156Seadler "%s: MGT (%d)\n", __func__, tp->mgmtrate); 3540330155Seadler } else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3541330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mcastrate); 3542330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3543330156Seadler "%s: MCAST (%d)\n", __func__, tp->mcastrate); 3544330155Seadler } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 3545330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->ucastrate); 3546330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3547330156Seadler "%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate); 3548330155Seadler } else if (m->m_flags & M_EAPOL) { 3549330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3550330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3551330156Seadler "%s: EAPOL\n", __func__); 3552330156Seadler } else if (type == IEEE80211_FC0_TYPE_DATA) { 3553330156Seadler int i; 3554330156Seadler 3555286441Srpaulo /* for data frames, use RS table */ 3556330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__); 3557330155Seadler /* XXX pass pktlen */ 3558286441Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 3559286441Srpaulo i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); 3560286441Srpaulo ridx = in->in_ridx[i]; 3561286441Srpaulo 3562286441Srpaulo /* This is the index into the programmed table */ 3563286441Srpaulo tx->initial_rate_index = i; 3564286441Srpaulo tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); 3565330156Seadler 3566286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3567286441Srpaulo "%s: start with i=%d, txrate %d\n", 3568286441Srpaulo __func__, i, iwm_rates[ridx].rate); 3569330156Seadler } else { 3570330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3571330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DEFAULT (%d)\n", 3572330156Seadler __func__, tp->mgmtrate); 3573286441Srpaulo } 3574286441Srpaulo 3575330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3576330156Seadler "%s: frame type=%d txrate %d\n", 3577330156Seadler __func__, type, iwm_rates[ridx].rate); 3578330156Seadler 3579286441Srpaulo rinfo = &iwm_rates[ridx]; 3580286441Srpaulo 3581286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n", 3582286441Srpaulo __func__, ridx, 3583286441Srpaulo rinfo->rate, 3584286441Srpaulo !! (IWM_RIDX_IS_CCK(ridx)) 3585286441Srpaulo ); 3586286441Srpaulo 3587286441Srpaulo /* XXX TODO: hard-coded TX antenna? */ 3588286441Srpaulo rate_flags = 1 << IWM_RATE_MCS_ANT_POS; 3589286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 3590286441Srpaulo rate_flags |= IWM_RATE_MCS_CCK_MSK; 3591286441Srpaulo tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); 3592286441Srpaulo 3593286441Srpaulo return rinfo; 3594286441Srpaulo} 3595286441Srpaulo 3596286441Srpaulo#define TB0_SIZE 16 3597286441Srpaulostatic int 3598286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) 3599286441Srpaulo{ 3600287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3601286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3602293099Savos struct iwm_node *in = IWM_NODE(ni); 3603286441Srpaulo struct iwm_tx_ring *ring; 3604286441Srpaulo struct iwm_tx_data *data; 3605286441Srpaulo struct iwm_tfd *desc; 3606286441Srpaulo struct iwm_device_cmd *cmd; 3607286441Srpaulo struct iwm_tx_cmd *tx; 3608286441Srpaulo struct ieee80211_frame *wh; 3609286441Srpaulo struct ieee80211_key *k = NULL; 3610286441Srpaulo struct mbuf *m1; 3611286441Srpaulo const struct iwm_rate *rinfo; 3612286441Srpaulo uint32_t flags; 3613286441Srpaulo u_int hdrlen; 3614286441Srpaulo bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER]; 3615286441Srpaulo int nsegs; 3616286441Srpaulo uint8_t tid, type; 3617286441Srpaulo int i, totlen, error, pad; 3618286441Srpaulo 3619286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3620286441Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3621286441Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3622286441Srpaulo tid = 0; 3623286441Srpaulo ring = &sc->txq[ac]; 3624286441Srpaulo desc = &ring->desc[ring->cur]; 3625286441Srpaulo memset(desc, 0, sizeof(*desc)); 3626286441Srpaulo data = &ring->data[ring->cur]; 3627286441Srpaulo 3628286441Srpaulo /* Fill out iwm_tx_cmd to send to the firmware */ 3629286441Srpaulo cmd = &ring->cmd[ring->cur]; 3630286441Srpaulo cmd->hdr.code = IWM_TX_CMD; 3631286441Srpaulo cmd->hdr.flags = 0; 3632286441Srpaulo cmd->hdr.qid = ring->qid; 3633286441Srpaulo cmd->hdr.idx = ring->cur; 3634286441Srpaulo 3635286441Srpaulo tx = (void *)cmd->data; 3636286441Srpaulo memset(tx, 0, sizeof(*tx)); 3637286441Srpaulo 3638330155Seadler rinfo = iwm_tx_fill_cmd(sc, in, m, tx); 3639286441Srpaulo 3640286441Srpaulo /* Encrypt the frame if need be. */ 3641286441Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 3642286441Srpaulo /* Retrieve key for TX && do software encryption. */ 3643286441Srpaulo k = ieee80211_crypto_encap(ni, m); 3644286441Srpaulo if (k == NULL) { 3645286441Srpaulo m_freem(m); 3646286441Srpaulo return (ENOBUFS); 3647286441Srpaulo } 3648286441Srpaulo /* 802.11 header may have moved. */ 3649286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3650286441Srpaulo } 3651286441Srpaulo 3652286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3653286441Srpaulo struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; 3654286441Srpaulo 3655286441Srpaulo tap->wt_flags = 0; 3656286441Srpaulo tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); 3657286441Srpaulo tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); 3658286441Srpaulo tap->wt_rate = rinfo->rate; 3659286441Srpaulo if (k != NULL) 3660286441Srpaulo tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 3661286441Srpaulo ieee80211_radiotap_tx(vap, m); 3662286441Srpaulo } 3663286441Srpaulo 3664286441Srpaulo 3665286441Srpaulo totlen = m->m_pkthdr.len; 3666286441Srpaulo 3667286441Srpaulo flags = 0; 3668286441Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3669286441Srpaulo flags |= IWM_TX_CMD_FLG_ACK; 3670286441Srpaulo } 3671286441Srpaulo 3672303628Ssbruno if (type == IEEE80211_FC0_TYPE_DATA 3673286441Srpaulo && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) 3674286441Srpaulo && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3675286441Srpaulo flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; 3676286441Srpaulo } 3677286441Srpaulo 3678286441Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3679286441Srpaulo type != IEEE80211_FC0_TYPE_DATA) 3680286441Srpaulo tx->sta_id = sc->sc_aux_sta.sta_id; 3681286441Srpaulo else 3682286441Srpaulo tx->sta_id = IWM_STATION_ID; 3683286441Srpaulo 3684286441Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 3685286441Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3686286441Srpaulo 3687286441Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3688303628Ssbruno subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 3689303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC); 3690303628Ssbruno } else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) { 3691303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3692303628Ssbruno } else { 3693303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT); 3694303628Ssbruno } 3695286441Srpaulo } else { 3696303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3697286441Srpaulo } 3698286441Srpaulo 3699286441Srpaulo if (hdrlen & 3) { 3700286441Srpaulo /* First segment length must be a multiple of 4. */ 3701286441Srpaulo flags |= IWM_TX_CMD_FLG_MH_PAD; 3702286441Srpaulo pad = 4 - (hdrlen & 3); 3703286441Srpaulo } else 3704286441Srpaulo pad = 0; 3705286441Srpaulo 3706286441Srpaulo tx->driver_txop = 0; 3707286441Srpaulo tx->next_frame_len = 0; 3708286441Srpaulo 3709286441Srpaulo tx->len = htole16(totlen); 3710286441Srpaulo tx->tid_tspec = tid; 3711286441Srpaulo tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); 3712286441Srpaulo 3713286441Srpaulo /* Set physical address of "scratch area". */ 3714286441Srpaulo tx->dram_lsb_ptr = htole32(data->scratch_paddr); 3715286441Srpaulo tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); 3716286441Srpaulo 3717286441Srpaulo /* Copy 802.11 header in TX command. */ 3718286441Srpaulo memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); 3719286441Srpaulo 3720286441Srpaulo flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; 3721286441Srpaulo 3722286441Srpaulo tx->sec_ctl = 0; 3723286441Srpaulo tx->tx_flags |= htole32(flags); 3724286441Srpaulo 3725286441Srpaulo /* Trim 802.11 header. */ 3726286441Srpaulo m_adj(m, hdrlen); 3727286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3728286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3729286441Srpaulo if (error != 0) { 3730286441Srpaulo if (error != EFBIG) { 3731286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3732286441Srpaulo error); 3733286441Srpaulo m_freem(m); 3734286441Srpaulo return error; 3735286441Srpaulo } 3736286441Srpaulo /* Too many DMA segments, linearize mbuf. */ 3737293119Savos m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2); 3738286441Srpaulo if (m1 == NULL) { 3739293119Savos device_printf(sc->sc_dev, 3740293119Savos "%s: could not defrag mbuf\n", __func__); 3741286441Srpaulo m_freem(m); 3742293119Savos return (ENOBUFS); 3743286441Srpaulo } 3744286441Srpaulo m = m1; 3745293119Savos 3746286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3747286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3748286441Srpaulo if (error != 0) { 3749286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3750286441Srpaulo error); 3751286441Srpaulo m_freem(m); 3752286441Srpaulo return error; 3753286441Srpaulo } 3754286441Srpaulo } 3755286441Srpaulo data->m = m; 3756286441Srpaulo data->in = in; 3757286441Srpaulo data->done = 0; 3758286441Srpaulo 3759286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3760286441Srpaulo "sending txd %p, in %p\n", data, data->in); 3761286441Srpaulo KASSERT(data->in != NULL, ("node is NULL")); 3762286441Srpaulo 3763286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3764303628Ssbruno "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n", 3765298611Sadrian ring->qid, ring->cur, totlen, nsegs, 3766298611Sadrian le32toh(tx->tx_flags), 3767298611Sadrian le32toh(tx->rate_n_flags), 3768303628Ssbruno tx->initial_rate_index 3769298611Sadrian ); 3770286441Srpaulo 3771286441Srpaulo /* Fill TX descriptor. */ 3772286441Srpaulo desc->num_tbs = 2 + nsegs; 3773286441Srpaulo 3774286441Srpaulo desc->tbs[0].lo = htole32(data->cmd_paddr); 3775286441Srpaulo desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3776286441Srpaulo (TB0_SIZE << 4); 3777286441Srpaulo desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); 3778286441Srpaulo desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3779286441Srpaulo ((sizeof(struct iwm_cmd_header) + sizeof(*tx) 3780286441Srpaulo + hdrlen + pad - TB0_SIZE) << 4); 3781286441Srpaulo 3782286441Srpaulo /* Other DMA segments are for data payload. */ 3783286441Srpaulo for (i = 0; i < nsegs; i++) { 3784286441Srpaulo seg = &segs[i]; 3785286441Srpaulo desc->tbs[i+2].lo = htole32(seg->ds_addr); 3786286441Srpaulo desc->tbs[i+2].hi_n_len = \ 3787286441Srpaulo htole16(iwm_get_dma_hi_addr(seg->ds_addr)) 3788286441Srpaulo | ((seg->ds_len) << 4); 3789286441Srpaulo } 3790286441Srpaulo 3791286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3792286441Srpaulo BUS_DMASYNC_PREWRITE); 3793286441Srpaulo bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, 3794286441Srpaulo BUS_DMASYNC_PREWRITE); 3795286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3796286441Srpaulo BUS_DMASYNC_PREWRITE); 3797286441Srpaulo 3798286441Srpaulo#if 0 3799286441Srpaulo iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); 3800286441Srpaulo#endif 3801286441Srpaulo 3802286441Srpaulo /* Kick TX ring. */ 3803286441Srpaulo ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; 3804286441Srpaulo IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3805286441Srpaulo 3806286441Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3807286441Srpaulo if (++ring->queued > IWM_TX_RING_HIMARK) { 3808286441Srpaulo sc->qfullmsk |= 1 << ring->qid; 3809286441Srpaulo } 3810286441Srpaulo 3811286441Srpaulo return 0; 3812286441Srpaulo} 3813286441Srpaulo 3814286441Srpaulostatic int 3815286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 3816286441Srpaulo const struct ieee80211_bpf_params *params) 3817286441Srpaulo{ 3818286441Srpaulo struct ieee80211com *ic = ni->ni_ic; 3819286865Sadrian struct iwm_softc *sc = ic->ic_softc; 3820286441Srpaulo int error = 0; 3821286441Srpaulo 3822286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3823286441Srpaulo "->%s begin\n", __func__); 3824286441Srpaulo 3825287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 3826286441Srpaulo m_freem(m); 3827286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3828286441Srpaulo "<-%s not RUNNING\n", __func__); 3829286441Srpaulo return (ENETDOWN); 3830286441Srpaulo } 3831286441Srpaulo 3832286441Srpaulo IWM_LOCK(sc); 3833286441Srpaulo /* XXX fix this */ 3834286441Srpaulo if (params == NULL) { 3835286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3836286441Srpaulo } else { 3837286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3838286441Srpaulo } 3839286441Srpaulo sc->sc_tx_timer = 5; 3840286441Srpaulo IWM_UNLOCK(sc); 3841286441Srpaulo 3842286441Srpaulo return (error); 3843286441Srpaulo} 3844286441Srpaulo 3845286441Srpaulo/* 3846286441Srpaulo * mvm/tx.c 3847286441Srpaulo */ 3848286441Srpaulo 3849286441Srpaulo/* 3850286441Srpaulo * Note that there are transports that buffer frames before they reach 3851286441Srpaulo * the firmware. This means that after flush_tx_path is called, the 3852286441Srpaulo * queue might not be empty. The race-free way to handle this is to: 3853286441Srpaulo * 1) set the station as draining 3854286441Srpaulo * 2) flush the Tx path 3855286441Srpaulo * 3) wait for the transport queues to be empty 3856286441Srpaulo */ 3857286441Srpauloint 3858330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags) 3859286441Srpaulo{ 3860330154Seadler int ret; 3861286441Srpaulo struct iwm_tx_path_flush_cmd flush_cmd = { 3862286441Srpaulo .queues_ctl = htole32(tfd_msk), 3863286441Srpaulo .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), 3864286441Srpaulo }; 3865286441Srpaulo 3866330154Seadler ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags, 3867286441Srpaulo sizeof(flush_cmd), &flush_cmd); 3868286441Srpaulo if (ret) 3869286441Srpaulo device_printf(sc->sc_dev, 3870286441Srpaulo "Flushing tx queue failed: %d\n", ret); 3871286441Srpaulo return ret; 3872286441Srpaulo} 3873286441Srpaulo 3874286441Srpaulo/* 3875286441Srpaulo * BEGIN mvm/quota.c 3876286441Srpaulo */ 3877286441Srpaulo 3878286441Srpaulostatic int 3879330203Seadleriwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_vap *ivp) 3880286441Srpaulo{ 3881286441Srpaulo struct iwm_time_quota_cmd cmd; 3882286441Srpaulo int i, idx, ret, num_active_macs, quota, quota_rem; 3883286441Srpaulo int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, }; 3884286441Srpaulo int n_ifs[IWM_MAX_BINDINGS] = {0, }; 3885286441Srpaulo uint16_t id; 3886286441Srpaulo 3887286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3888286441Srpaulo 3889286441Srpaulo /* currently, PHY ID == binding ID */ 3890330203Seadler if (ivp) { 3891330203Seadler id = ivp->phy_ctxt->id; 3892286441Srpaulo KASSERT(id < IWM_MAX_BINDINGS, ("invalid id")); 3893330203Seadler colors[id] = ivp->phy_ctxt->color; 3894286441Srpaulo 3895286441Srpaulo if (1) 3896286441Srpaulo n_ifs[id] = 1; 3897286441Srpaulo } 3898286441Srpaulo 3899286441Srpaulo /* 3900286441Srpaulo * The FW's scheduling session consists of 3901286441Srpaulo * IWM_MVM_MAX_QUOTA fragments. Divide these fragments 3902286441Srpaulo * equally between all the bindings that require quota 3903286441Srpaulo */ 3904286441Srpaulo num_active_macs = 0; 3905286441Srpaulo for (i = 0; i < IWM_MAX_BINDINGS; i++) { 3906286441Srpaulo cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID); 3907286441Srpaulo num_active_macs += n_ifs[i]; 3908286441Srpaulo } 3909286441Srpaulo 3910286441Srpaulo quota = 0; 3911286441Srpaulo quota_rem = 0; 3912286441Srpaulo if (num_active_macs) { 3913286441Srpaulo quota = IWM_MVM_MAX_QUOTA / num_active_macs; 3914286441Srpaulo quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs; 3915286441Srpaulo } 3916286441Srpaulo 3917286441Srpaulo for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) { 3918286441Srpaulo if (colors[i] < 0) 3919286441Srpaulo continue; 3920286441Srpaulo 3921286441Srpaulo cmd.quotas[idx].id_and_color = 3922286441Srpaulo htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i])); 3923286441Srpaulo 3924286441Srpaulo if (n_ifs[i] <= 0) { 3925286441Srpaulo cmd.quotas[idx].quota = htole32(0); 3926286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3927286441Srpaulo } else { 3928286441Srpaulo cmd.quotas[idx].quota = htole32(quota * n_ifs[i]); 3929286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3930286441Srpaulo } 3931286441Srpaulo idx++; 3932286441Srpaulo } 3933286441Srpaulo 3934286441Srpaulo /* Give the remainder of the session to the first binding */ 3935286441Srpaulo cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem); 3936286441Srpaulo 3937286441Srpaulo ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC, 3938286441Srpaulo sizeof(cmd), &cmd); 3939286441Srpaulo if (ret) 3940286441Srpaulo device_printf(sc->sc_dev, 3941286441Srpaulo "%s: Failed to send quota: %d\n", __func__, ret); 3942286441Srpaulo return ret; 3943286441Srpaulo} 3944286441Srpaulo 3945286441Srpaulo/* 3946286441Srpaulo * END mvm/quota.c 3947286441Srpaulo */ 3948286441Srpaulo 3949286441Srpaulo/* 3950286441Srpaulo * ieee80211 routines 3951286441Srpaulo */ 3952286441Srpaulo 3953286441Srpaulo/* 3954286441Srpaulo * Change to AUTH state in 80211 state machine. Roughly matches what 3955286441Srpaulo * Linux does in bss_info_changed(). 3956286441Srpaulo */ 3957286441Srpaulostatic int 3958286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) 3959286441Srpaulo{ 3960286441Srpaulo struct ieee80211_node *ni; 3961286441Srpaulo struct iwm_node *in; 3962286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 3963286441Srpaulo uint32_t duration; 3964286441Srpaulo int error; 3965286441Srpaulo 3966286441Srpaulo /* 3967286441Srpaulo * XXX i have a feeling that the vap node is being 3968286441Srpaulo * freed from underneath us. Grr. 3969286441Srpaulo */ 3970286441Srpaulo ni = ieee80211_ref_node(vap->iv_bss); 3971293099Savos in = IWM_NODE(ni); 3972286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE, 3973286441Srpaulo "%s: called; vap=%p, bss ni=%p\n", 3974286441Srpaulo __func__, 3975286441Srpaulo vap, 3976286441Srpaulo ni); 3977286441Srpaulo 3978286441Srpaulo in->in_assoc = 0; 3979286441Srpaulo 3980330212Seadler /* 3981330212Seadler * Firmware bug - it'll crash if the beacon interval is less 3982330212Seadler * than 16. We can't avoid connecting at all, so refuse the 3983330212Seadler * station state change, this will cause net80211 to abandon 3984330212Seadler * attempts to connect to this AP, and eventually wpa_s will 3985330212Seadler * blacklist the AP... 3986330212Seadler */ 3987330212Seadler if (ni->ni_intval < 16) { 3988330212Seadler device_printf(sc->sc_dev, 3989330212Seadler "AP %s beacon interval is %d, refusing due to firmware bug!\n", 3990330212Seadler ether_sprintf(ni->ni_bssid), ni->ni_intval); 3991330212Seadler error = EINVAL; 3992330212Seadler goto out; 3993330212Seadler } 3994330212Seadler 3995303628Ssbruno error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON); 3996303628Ssbruno if (error != 0) 3997303628Ssbruno return error; 3998303628Ssbruno 3999286441Srpaulo error = iwm_allow_mcast(vap, sc); 4000286441Srpaulo if (error) { 4001286441Srpaulo device_printf(sc->sc_dev, 4002286441Srpaulo "%s: failed to set multicast\n", __func__); 4003286441Srpaulo goto out; 4004286441Srpaulo } 4005286441Srpaulo 4006286441Srpaulo /* 4007286441Srpaulo * This is where it deviates from what Linux does. 4008286441Srpaulo * 4009286441Srpaulo * Linux iwlwifi doesn't reset the nic each time, nor does it 4010286441Srpaulo * call ctxt_add() here. Instead, it adds it during vap creation, 4011303628Ssbruno * and always does a mac_ctx_changed(). 4012286441Srpaulo * 4013286441Srpaulo * The openbsd port doesn't attempt to do that - it reset things 4014286441Srpaulo * at odd states and does the add here. 4015286441Srpaulo * 4016286441Srpaulo * So, until the state handling is fixed (ie, we never reset 4017286441Srpaulo * the NIC except for a firmware failure, which should drag 4018286441Srpaulo * the NIC back to IDLE, re-setup and re-add all the mac/phy 4019286441Srpaulo * contexts that are required), let's do a dirty hack here. 4020286441Srpaulo */ 4021286441Srpaulo if (iv->is_uploaded) { 4022286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 4023286441Srpaulo device_printf(sc->sc_dev, 4024298582Sadrian "%s: failed to update MAC\n", __func__); 4025286441Srpaulo goto out; 4026286441Srpaulo } 4027298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 4028298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 4029298582Sadrian device_printf(sc->sc_dev, 4030298582Sadrian "%s: failed update phy ctxt\n", __func__); 4031298582Sadrian goto out; 4032298582Sadrian } 4033330203Seadler iv->phy_ctxt = &sc->sc_phyctxt[0]; 4034298582Sadrian 4035330204Seadler if ((error = iwm_mvm_binding_add_vif(sc, iv)) != 0) { 4036298582Sadrian device_printf(sc->sc_dev, 4037298582Sadrian "%s: binding update cmd\n", __func__); 4038298582Sadrian goto out; 4039298582Sadrian } 4040298582Sadrian if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 4041298582Sadrian device_printf(sc->sc_dev, 4042298582Sadrian "%s: failed to update sta\n", __func__); 4043298582Sadrian goto out; 4044298582Sadrian } 4045286441Srpaulo } else { 4046286441Srpaulo if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) { 4047286441Srpaulo device_printf(sc->sc_dev, 4048286441Srpaulo "%s: failed to add MAC\n", __func__); 4049286441Srpaulo goto out; 4050286441Srpaulo } 4051330201Seadler if ((error = iwm_mvm_power_update_mac(sc)) != 0) { 4052330201Seadler device_printf(sc->sc_dev, 4053330201Seadler "%s: failed to update power management\n", 4054330201Seadler __func__); 4055330201Seadler goto out; 4056330201Seadler } 4057298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 4058298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 4059286441Srpaulo device_printf(sc->sc_dev, 4060298582Sadrian "%s: failed add phy ctxt!\n", __func__); 4061286441Srpaulo error = ETIMEDOUT; 4062286441Srpaulo goto out; 4063298582Sadrian } 4064330203Seadler iv->phy_ctxt = &sc->sc_phyctxt[0]; 4065298582Sadrian 4066330203Seadler if ((error = iwm_mvm_binding_add_vif(sc, iv)) != 0) { 4067286441Srpaulo device_printf(sc->sc_dev, 4068298582Sadrian "%s: binding add cmd\n", __func__); 4069286441Srpaulo goto out; 4070286441Srpaulo } 4071298582Sadrian if ((error = iwm_mvm_add_sta(sc, in)) != 0) { 4072298582Sadrian device_printf(sc->sc_dev, 4073298582Sadrian "%s: failed to add sta\n", __func__); 4074298582Sadrian goto out; 4075298582Sadrian } 4076286441Srpaulo } 4077298582Sadrian 4078298582Sadrian /* 4079298582Sadrian * Prevent the FW from wandering off channel during association 4080298582Sadrian * by "protecting" the session with a time event. 4081298582Sadrian */ 4082298582Sadrian /* XXX duration is in units of TU, not MS */ 4083298582Sadrian duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; 4084330204Seadler iwm_mvm_protect_session(sc, iv, duration, 500 /* XXX magic number */); 4085298582Sadrian DELAY(100); 4086298582Sadrian 4087286441Srpaulo error = 0; 4088286441Srpauloout: 4089286441Srpaulo ieee80211_free_node(ni); 4090286441Srpaulo return (error); 4091286441Srpaulo} 4092286441Srpaulo 4093286441Srpaulostatic int 4094286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc) 4095286441Srpaulo{ 4096293099Savos struct iwm_node *in = IWM_NODE(vap->iv_bss); 4097286441Srpaulo int error; 4098286441Srpaulo 4099286441Srpaulo if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 4100286441Srpaulo device_printf(sc->sc_dev, 4101286441Srpaulo "%s: failed to update STA\n", __func__); 4102286441Srpaulo return error; 4103286441Srpaulo } 4104286441Srpaulo 4105286441Srpaulo in->in_assoc = 1; 4106286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 4107286441Srpaulo device_printf(sc->sc_dev, 4108286441Srpaulo "%s: failed to update MAC\n", __func__); 4109286441Srpaulo return error; 4110286441Srpaulo } 4111286441Srpaulo 4112286441Srpaulo return 0; 4113286441Srpaulo} 4114286441Srpaulo 4115286441Srpaulostatic int 4116286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in) 4117286441Srpaulo{ 4118330154Seadler uint32_t tfd_msk; 4119330154Seadler 4120286441Srpaulo /* 4121286441Srpaulo * Ok, so *technically* the proper set of calls for going 4122286441Srpaulo * from RUN back to SCAN is: 4123286441Srpaulo * 4124286441Srpaulo * iwm_mvm_power_mac_disable(sc, in); 4125330209Seadler * iwm_mvm_mac_ctxt_changed(sc, vap); 4126286441Srpaulo * iwm_mvm_rm_sta(sc, in); 4127286441Srpaulo * iwm_mvm_update_quotas(sc, NULL); 4128286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 4129330204Seadler * iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap)); 4130286441Srpaulo * iwm_mvm_mac_ctxt_remove(sc, in); 4131286441Srpaulo * 4132286441Srpaulo * However, that freezes the device not matter which permutations 4133286441Srpaulo * and modifications are attempted. Obviously, this driver is missing 4134286441Srpaulo * something since it works in the Linux driver, but figuring out what 4135286441Srpaulo * is missing is a little more complicated. Now, since we're going 4136286441Srpaulo * back to nothing anyway, we'll just do a complete device reset. 4137286441Srpaulo * Up your's, device! 4138286441Srpaulo */ 4139330154Seadler /* 4140330154Seadler * Just using 0xf for the queues mask is fine as long as we only 4141330154Seadler * get here from RUN state. 4142330154Seadler */ 4143330154Seadler tfd_msk = 0xf; 4144330223Seadler iwm_xmit_queue_drain(sc); 4145330154Seadler iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC); 4146330154Seadler /* 4147330154Seadler * We seem to get away with just synchronously sending the 4148330154Seadler * IWM_TXPATH_FLUSH command. 4149330154Seadler */ 4150330154Seadler// iwm_trans_wait_tx_queue_empty(sc, tfd_msk); 4151286441Srpaulo iwm_stop_device(sc); 4152286441Srpaulo iwm_init_hw(sc); 4153286441Srpaulo if (in) 4154286441Srpaulo in->in_assoc = 0; 4155286441Srpaulo return 0; 4156286441Srpaulo 4157286441Srpaulo#if 0 4158286441Srpaulo int error; 4159286441Srpaulo 4160286441Srpaulo iwm_mvm_power_mac_disable(sc, in); 4161286441Srpaulo 4162330209Seadler if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 4163286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); 4164286441Srpaulo return error; 4165286441Srpaulo } 4166286441Srpaulo 4167286441Srpaulo if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { 4168286441Srpaulo device_printf(sc->sc_dev, "sta remove fail %d\n", error); 4169286441Srpaulo return error; 4170286441Srpaulo } 4171286441Srpaulo error = iwm_mvm_rm_sta(sc, in); 4172286441Srpaulo in->in_assoc = 0; 4173286441Srpaulo iwm_mvm_update_quotas(sc, NULL); 4174330209Seadler if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 4175286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); 4176286441Srpaulo return error; 4177286441Srpaulo } 4178330204Seadler iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap)); 4179286441Srpaulo 4180286441Srpaulo iwm_mvm_mac_ctxt_remove(sc, in); 4181286441Srpaulo 4182286441Srpaulo return error; 4183286441Srpaulo#endif 4184286441Srpaulo} 4185286441Srpaulo 4186286441Srpaulostatic struct ieee80211_node * 4187286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 4188286441Srpaulo{ 4189286441Srpaulo return malloc(sizeof (struct iwm_node), M_80211_NODE, 4190286441Srpaulo M_NOWAIT | M_ZERO); 4191286441Srpaulo} 4192286441Srpaulo 4193330198Seadleruint8_t 4194330198Seadleriwm_ridx2rate(struct ieee80211_rateset *rs, int ridx) 4195330198Seadler{ 4196330198Seadler int i; 4197330198Seadler uint8_t rval; 4198330198Seadler 4199330198Seadler for (i = 0; i < rs->rs_nrates; i++) { 4200330198Seadler rval = (rs->rs_rates[i] & IEEE80211_RATE_VAL); 4201330198Seadler if (rval == iwm_rates[ridx].rate) 4202330198Seadler return rs->rs_rates[i]; 4203330198Seadler } 4204330198Seadler 4205330198Seadler return 0; 4206330198Seadler} 4207330198Seadler 4208286441Srpaulostatic void 4209286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in) 4210286441Srpaulo{ 4211286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 4212286441Srpaulo struct iwm_lq_cmd *lq = &in->in_lq; 4213286441Srpaulo int nrates = ni->ni_rates.rs_nrates; 4214286441Srpaulo int i, ridx, tab = 0; 4215330156Seadler// int txant = 0; 4216286441Srpaulo 4217286441Srpaulo if (nrates > nitems(lq->rs_table)) { 4218286441Srpaulo device_printf(sc->sc_dev, 4219286441Srpaulo "%s: node supports %d rates, driver handles " 4220286441Srpaulo "only %zu\n", __func__, nrates, nitems(lq->rs_table)); 4221286441Srpaulo return; 4222286441Srpaulo } 4223294248Sadrian if (nrates == 0) { 4224294248Sadrian device_printf(sc->sc_dev, 4225294248Sadrian "%s: node supports 0 rates, odd!\n", __func__); 4226294248Sadrian return; 4227294248Sadrian } 4228286441Srpaulo 4229286441Srpaulo /* 4230286441Srpaulo * XXX .. and most of iwm_node is not initialised explicitly; 4231286441Srpaulo * it's all just 0x0 passed to the firmware. 4232286441Srpaulo */ 4233286441Srpaulo 4234286441Srpaulo /* first figure out which rates we should support */ 4235286441Srpaulo /* XXX TODO: this isn't 11n aware /at all/ */ 4236286441Srpaulo memset(&in->in_ridx, -1, sizeof(in->in_ridx)); 4237286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4238286441Srpaulo "%s: nrates=%d\n", __func__, nrates); 4239286441Srpaulo 4240294248Sadrian /* 4241294248Sadrian * Loop over nrates and populate in_ridx from the highest 4242294248Sadrian * rate to the lowest rate. Remember, in_ridx[] has 4243294248Sadrian * IEEE80211_RATE_MAXSIZE entries! 4244294248Sadrian */ 4245294248Sadrian for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) { 4246294248Sadrian int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL; 4247294248Sadrian 4248286441Srpaulo /* Map 802.11 rate to HW rate index. */ 4249286441Srpaulo for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) 4250286441Srpaulo if (iwm_rates[ridx].rate == rate) 4251286441Srpaulo break; 4252286441Srpaulo if (ridx > IWM_RIDX_MAX) { 4253286441Srpaulo device_printf(sc->sc_dev, 4254286441Srpaulo "%s: WARNING: device rate for %d not found!\n", 4255286441Srpaulo __func__, rate); 4256286441Srpaulo } else { 4257286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4258286441Srpaulo "%s: rate: i: %d, rate=%d, ridx=%d\n", 4259286441Srpaulo __func__, 4260286441Srpaulo i, 4261286441Srpaulo rate, 4262286441Srpaulo ridx); 4263286441Srpaulo in->in_ridx[i] = ridx; 4264286441Srpaulo } 4265286441Srpaulo } 4266286441Srpaulo 4267286441Srpaulo /* then construct a lq_cmd based on those */ 4268286441Srpaulo memset(lq, 0, sizeof(*lq)); 4269286441Srpaulo lq->sta_id = IWM_STATION_ID; 4270286441Srpaulo 4271303628Ssbruno /* For HT, always enable RTS/CTS to avoid excessive retries. */ 4272303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) 4273303628Ssbruno lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; 4274303628Ssbruno 4275286441Srpaulo /* 4276286441Srpaulo * are these used? (we don't do SISO or MIMO) 4277286441Srpaulo * need to set them to non-zero, though, or we get an error. 4278286441Srpaulo */ 4279286441Srpaulo lq->single_stream_ant_msk = 1; 4280286441Srpaulo lq->dual_stream_ant_msk = 1; 4281286441Srpaulo 4282286441Srpaulo /* 4283286441Srpaulo * Build the actual rate selection table. 4284286441Srpaulo * The lowest bits are the rates. Additionally, 4285286441Srpaulo * CCK needs bit 9 to be set. The rest of the bits 4286286441Srpaulo * we add to the table select the tx antenna 4287286441Srpaulo * Note that we add the rates in the highest rate first 4288286441Srpaulo * (opposite of ni_rates). 4289286441Srpaulo */ 4290286441Srpaulo /* 4291286441Srpaulo * XXX TODO: this should be looping over the min of nrates 4292286441Srpaulo * and LQ_MAX_RETRY_NUM. Sigh. 4293286441Srpaulo */ 4294286441Srpaulo for (i = 0; i < nrates; i++) { 4295286441Srpaulo int nextant; 4296286441Srpaulo 4297330156Seadler#if 0 4298286441Srpaulo if (txant == 0) 4299330167Seadler txant = iwm_mvm_get_valid_tx_ant(sc); 4300286441Srpaulo nextant = 1<<(ffs(txant)-1); 4301286441Srpaulo txant &= ~nextant; 4302330156Seadler#else 4303330167Seadler nextant = iwm_mvm_get_valid_tx_ant(sc); 4304330156Seadler#endif 4305286441Srpaulo /* 4306286441Srpaulo * Map the rate id into a rate index into 4307286441Srpaulo * our hardware table containing the 4308286441Srpaulo * configuration to use for this rate. 4309286441Srpaulo */ 4310294248Sadrian ridx = in->in_ridx[i]; 4311286441Srpaulo tab = iwm_rates[ridx].plcp; 4312286441Srpaulo tab |= nextant << IWM_RATE_MCS_ANT_POS; 4313286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 4314286441Srpaulo tab |= IWM_RATE_MCS_CCK_MSK; 4315286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4316286441Srpaulo "station rate i=%d, rate=%d, hw=%x\n", 4317286441Srpaulo i, iwm_rates[ridx].rate, tab); 4318286441Srpaulo lq->rs_table[i] = htole32(tab); 4319286441Srpaulo } 4320286441Srpaulo /* then fill the rest with the lowest possible rate */ 4321286441Srpaulo for (i = nrates; i < nitems(lq->rs_table); i++) { 4322286441Srpaulo KASSERT(tab != 0, ("invalid tab")); 4323286441Srpaulo lq->rs_table[i] = htole32(tab); 4324286441Srpaulo } 4325286441Srpaulo} 4326286441Srpaulo 4327286441Srpaulostatic int 4328286441Srpauloiwm_media_change(struct ifnet *ifp) 4329286441Srpaulo{ 4330287197Sglebius struct ieee80211vap *vap = ifp->if_softc; 4331287197Sglebius struct ieee80211com *ic = vap->iv_ic; 4332287197Sglebius struct iwm_softc *sc = ic->ic_softc; 4333286441Srpaulo int error; 4334286441Srpaulo 4335286441Srpaulo error = ieee80211_media_change(ifp); 4336286441Srpaulo if (error != ENETRESET) 4337286441Srpaulo return error; 4338286441Srpaulo 4339287197Sglebius IWM_LOCK(sc); 4340287197Sglebius if (ic->ic_nrunning > 0) { 4341287197Sglebius iwm_stop(sc); 4342286441Srpaulo iwm_init(sc); 4343286441Srpaulo } 4344287197Sglebius IWM_UNLOCK(sc); 4345286441Srpaulo return error; 4346286441Srpaulo} 4347286441Srpaulo 4348286441Srpaulo 4349286441Srpaulostatic int 4350286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 4351286441Srpaulo{ 4352286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 4353286441Srpaulo struct ieee80211com *ic = vap->iv_ic; 4354286865Sadrian struct iwm_softc *sc = ic->ic_softc; 4355286441Srpaulo struct iwm_node *in; 4356286441Srpaulo int error; 4357286441Srpaulo 4358286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4359286441Srpaulo "switching state %s -> %s\n", 4360286441Srpaulo ieee80211_state_name[vap->iv_state], 4361286441Srpaulo ieee80211_state_name[nstate]); 4362286441Srpaulo IEEE80211_UNLOCK(ic); 4363286441Srpaulo IWM_LOCK(sc); 4364301187Sadrian 4365301187Sadrian if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state) 4366301187Sadrian iwm_led_blink_stop(sc); 4367301187Sadrian 4368286441Srpaulo /* disable beacon filtering if we're hopping out of RUN */ 4369286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { 4370286441Srpaulo iwm_mvm_disable_beacon_filter(sc); 4371286441Srpaulo 4372293099Savos if (((in = IWM_NODE(vap->iv_bss)) != NULL)) 4373286441Srpaulo in->in_assoc = 0; 4374286441Srpaulo 4375330154Seadler if (nstate == IEEE80211_S_INIT) { 4376330154Seadler IWM_UNLOCK(sc); 4377330154Seadler IEEE80211_LOCK(ic); 4378330154Seadler error = ivp->iv_newstate(vap, nstate, arg); 4379330154Seadler IEEE80211_UNLOCK(ic); 4380330154Seadler IWM_LOCK(sc); 4381330154Seadler iwm_release(sc, NULL); 4382330154Seadler IWM_UNLOCK(sc); 4383330154Seadler IEEE80211_LOCK(ic); 4384330154Seadler return error; 4385330154Seadler } 4386286441Srpaulo 4387286441Srpaulo /* 4388286441Srpaulo * It's impossible to directly go RUN->SCAN. If we iwm_release() 4389286441Srpaulo * above then the card will be completely reinitialized, 4390286441Srpaulo * so the driver must do everything necessary to bring the card 4391286441Srpaulo * from INIT to SCAN. 4392286441Srpaulo * 4393286441Srpaulo * Additionally, upon receiving deauth frame from AP, 4394286441Srpaulo * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH 4395286441Srpaulo * state. This will also fail with this driver, so bring the FSM 4396286441Srpaulo * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. 4397286441Srpaulo * 4398286441Srpaulo * XXX TODO: fix this for FreeBSD! 4399286441Srpaulo */ 4400286441Srpaulo if (nstate == IEEE80211_S_SCAN || 4401286441Srpaulo nstate == IEEE80211_S_AUTH || 4402286441Srpaulo nstate == IEEE80211_S_ASSOC) { 4403286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4404286441Srpaulo "Force transition to INIT; MGT=%d\n", arg); 4405286441Srpaulo IWM_UNLOCK(sc); 4406286441Srpaulo IEEE80211_LOCK(ic); 4407303628Ssbruno /* Always pass arg as -1 since we can't Tx right now. */ 4408303628Ssbruno /* 4409303628Ssbruno * XXX arg is just ignored anyway when transitioning 4410303628Ssbruno * to IEEE80211_S_INIT. 4411303628Ssbruno */ 4412303628Ssbruno vap->iv_newstate(vap, IEEE80211_S_INIT, -1); 4413286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4414286441Srpaulo "Going INIT->SCAN\n"); 4415286441Srpaulo nstate = IEEE80211_S_SCAN; 4416286441Srpaulo IEEE80211_UNLOCK(ic); 4417286441Srpaulo IWM_LOCK(sc); 4418286441Srpaulo } 4419286441Srpaulo } 4420286441Srpaulo 4421286441Srpaulo switch (nstate) { 4422286441Srpaulo case IEEE80211_S_INIT: 4423330211Seadler case IEEE80211_S_SCAN: 4424330211Seadler if (vap->iv_state == IEEE80211_S_AUTH || 4425330211Seadler vap->iv_state == IEEE80211_S_ASSOC) { 4426330211Seadler int myerr; 4427330211Seadler IWM_UNLOCK(sc); 4428330211Seadler IEEE80211_LOCK(ic); 4429330211Seadler myerr = ivp->iv_newstate(vap, nstate, arg); 4430330211Seadler IEEE80211_UNLOCK(ic); 4431330211Seadler IWM_LOCK(sc); 4432330213Seadler error = iwm_mvm_rm_sta(sc, vap, FALSE); 4433330211Seadler if (error) { 4434330211Seadler device_printf(sc->sc_dev, 4435330211Seadler "%s: Failed to remove station: %d\n", 4436330211Seadler __func__, error); 4437330211Seadler } 4438330211Seadler error = iwm_mvm_mac_ctxt_changed(sc, vap); 4439330211Seadler if (error) { 4440330211Seadler device_printf(sc->sc_dev, 4441330211Seadler "%s: Failed to change mac context: %d\n", 4442330211Seadler __func__, error); 4443330211Seadler } 4444330211Seadler error = iwm_mvm_binding_remove_vif(sc, ivp); 4445330211Seadler if (error) { 4446330211Seadler device_printf(sc->sc_dev, 4447330211Seadler "%s: Failed to remove channel ctx: %d\n", 4448330211Seadler __func__, error); 4449330211Seadler } 4450330211Seadler ivp->phy_ctxt = NULL; 4451330211Seadler IWM_UNLOCK(sc); 4452330211Seadler IEEE80211_LOCK(ic); 4453330211Seadler return myerr; 4454330211Seadler } 4455286441Srpaulo break; 4456286441Srpaulo 4457286441Srpaulo case IEEE80211_S_AUTH: 4458286441Srpaulo if ((error = iwm_auth(vap, sc)) != 0) { 4459286441Srpaulo device_printf(sc->sc_dev, 4460286441Srpaulo "%s: could not move to auth state: %d\n", 4461286441Srpaulo __func__, error); 4462286441Srpaulo break; 4463286441Srpaulo } 4464286441Srpaulo break; 4465286441Srpaulo 4466286441Srpaulo case IEEE80211_S_ASSOC: 4467286441Srpaulo if ((error = iwm_assoc(vap, sc)) != 0) { 4468286441Srpaulo device_printf(sc->sc_dev, 4469286441Srpaulo "%s: failed to associate: %d\n", __func__, 4470286441Srpaulo error); 4471286441Srpaulo break; 4472286441Srpaulo } 4473286441Srpaulo break; 4474286441Srpaulo 4475286441Srpaulo case IEEE80211_S_RUN: 4476286441Srpaulo /* Update the association state, now we have it all */ 4477286441Srpaulo /* (eg associd comes in at this point */ 4478286441Srpaulo error = iwm_assoc(vap, sc); 4479286441Srpaulo if (error != 0) { 4480286441Srpaulo device_printf(sc->sc_dev, 4481286441Srpaulo "%s: failed to update association state: %d\n", 4482286441Srpaulo __func__, 4483286441Srpaulo error); 4484286441Srpaulo break; 4485286441Srpaulo } 4486286441Srpaulo 4487293099Savos in = IWM_NODE(vap->iv_bss); 4488286441Srpaulo iwm_mvm_enable_beacon_filter(sc, in); 4489330201Seadler iwm_mvm_power_update_mac(sc); 4490330203Seadler iwm_mvm_update_quotas(sc, ivp); 4491286441Srpaulo iwm_setrates(sc, in); 4492286441Srpaulo 4493330225Seadler if ((error = iwm_mvm_send_lq_cmd(sc, &in->in_lq, TRUE)) != 0) { 4494286441Srpaulo device_printf(sc->sc_dev, 4495330225Seadler "%s: IWM_LQ_CMD failed: %d\n", __func__, error); 4496286441Srpaulo } 4497286441Srpaulo 4498303628Ssbruno iwm_mvm_led_enable(sc); 4499286441Srpaulo break; 4500286441Srpaulo 4501286441Srpaulo default: 4502286441Srpaulo break; 4503286441Srpaulo } 4504286441Srpaulo IWM_UNLOCK(sc); 4505286441Srpaulo IEEE80211_LOCK(ic); 4506286441Srpaulo 4507286441Srpaulo return (ivp->iv_newstate(vap, nstate, arg)); 4508286441Srpaulo} 4509286441Srpaulo 4510286441Srpaulovoid 4511286441Srpauloiwm_endscan_cb(void *arg, int pending) 4512286441Srpaulo{ 4513286441Srpaulo struct iwm_softc *sc = arg; 4514287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4515286441Srpaulo 4516286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE, 4517286441Srpaulo "%s: scan ended\n", 4518286441Srpaulo __func__); 4519286441Srpaulo 4520303628Ssbruno ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); 4521303628Ssbruno} 4522303628Ssbruno 4523303628Ssbruno/* 4524303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4525303628Ssbruno * in default configuration 4526303628Ssbruno */ 4527303628Ssbrunostatic const uint32_t 4528303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4529303628Ssbruno { 4530303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF), 4531303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF) 4532303628Ssbruno }, 4533303628Ssbruno { 4534303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF), 4535303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF) 4536303628Ssbruno }, 4537303628Ssbruno { 4538303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER_DEF), 4539303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER_DEF) 4540303628Ssbruno }, 4541303628Ssbruno { 4542303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER_DEF), 4543303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER_DEF) 4544303628Ssbruno }, 4545303628Ssbruno { 4546303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER_DEF), 4547303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF) 4548303628Ssbruno }, 4549303628Ssbruno}; 4550303628Ssbruno 4551303628Ssbruno/* 4552303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4553303628Ssbruno * in single BSS MAC configuration. 4554303628Ssbruno */ 4555303628Ssbrunostatic const uint32_t 4556303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4557303628Ssbruno { 4558303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER), 4559303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER) 4560303628Ssbruno }, 4561303628Ssbruno { 4562303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER), 4563303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER) 4564303628Ssbruno }, 4565303628Ssbruno { 4566303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER), 4567303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER) 4568303628Ssbruno }, 4569303628Ssbruno { 4570303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER), 4571303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER) 4572303628Ssbruno }, 4573303628Ssbruno { 4574303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER), 4575303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER) 4576303628Ssbruno }, 4577303628Ssbruno}; 4578303628Ssbruno 4579303628Ssbrunostatic void 4580303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd, 4581303628Ssbruno struct ieee80211_node *ni) 4582303628Ssbruno{ 4583303628Ssbruno int i, j, watermark; 4584303628Ssbruno 4585303628Ssbruno sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN); 4586303628Ssbruno 4587303628Ssbruno /* 4588303628Ssbruno * If we are in association flow - check antenna configuration 4589303628Ssbruno * capabilities of the AP station, and choose the watermark accordingly. 4590303628Ssbruno */ 4591303628Ssbruno if (ni) { 4592303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) { 4593303628Ssbruno#ifdef notyet 4594303628Ssbruno if (ni->ni_rxmcs[2] != 0) 4595303628Ssbruno watermark = IWM_SF_W_MARK_MIMO3; 4596303628Ssbruno else if (ni->ni_rxmcs[1] != 0) 4597303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4598303628Ssbruno else 4599303628Ssbruno#endif 4600303628Ssbruno watermark = IWM_SF_W_MARK_SISO; 4601303628Ssbruno } else { 4602303628Ssbruno watermark = IWM_SF_W_MARK_LEGACY; 4603286441Srpaulo } 4604303628Ssbruno /* default watermark value for unassociated mode. */ 4605286441Srpaulo } else { 4606303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4607286441Srpaulo } 4608303628Ssbruno sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark); 4609286441Srpaulo 4610303628Ssbruno for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) { 4611303628Ssbruno for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) { 4612303628Ssbruno sf_cmd->long_delay_timeouts[i][j] = 4613303628Ssbruno htole32(IWM_SF_LONG_DELAY_AGING_TIMER); 4614303628Ssbruno } 4615286441Srpaulo } 4616303628Ssbruno 4617303628Ssbruno if (ni) { 4618303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout, 4619303628Ssbruno sizeof(iwm_sf_full_timeout)); 4620303628Ssbruno } else { 4621303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def, 4622303628Ssbruno sizeof(iwm_sf_full_timeout_def)); 4623303628Ssbruno } 4624286441Srpaulo} 4625286441Srpaulo 4626286441Srpaulostatic int 4627303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) 4628303628Ssbruno{ 4629303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 4630303628Ssbruno struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4631303628Ssbruno struct iwm_sf_cfg_cmd sf_cmd = { 4632303628Ssbruno .state = htole32(IWM_SF_FULL_ON), 4633303628Ssbruno }; 4634303628Ssbruno int ret = 0; 4635303628Ssbruno 4636330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 4637303628Ssbruno sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF); 4638303628Ssbruno 4639303628Ssbruno switch (new_state) { 4640303628Ssbruno case IWM_SF_UNINIT: 4641303628Ssbruno case IWM_SF_INIT_OFF: 4642303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL); 4643303628Ssbruno break; 4644303628Ssbruno case IWM_SF_FULL_ON: 4645303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss); 4646303628Ssbruno break; 4647303628Ssbruno default: 4648303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE, 4649303628Ssbruno "Invalid state: %d. not sending Smart Fifo cmd\n", 4650303628Ssbruno new_state); 4651303628Ssbruno return EINVAL; 4652303628Ssbruno } 4653303628Ssbruno 4654303628Ssbruno ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC, 4655303628Ssbruno sizeof(sf_cmd), &sf_cmd); 4656303628Ssbruno return ret; 4657303628Ssbruno} 4658303628Ssbruno 4659303628Ssbrunostatic int 4660303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc) 4661303628Ssbruno{ 4662303628Ssbruno struct iwm_bt_coex_cmd bt_cmd; 4663303628Ssbruno 4664303628Ssbruno bt_cmd.mode = htole32(IWM_BT_COEX_WIFI); 4665303628Ssbruno bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET); 4666303628Ssbruno 4667303628Ssbruno return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd), 4668303628Ssbruno &bt_cmd); 4669303628Ssbruno} 4670303628Ssbruno 4671330217Seadlerstatic boolean_t 4672330217Seadleriwm_mvm_is_lar_supported(struct iwm_softc *sc) 4673330217Seadler{ 4674330217Seadler boolean_t nvm_lar = sc->nvm_data->lar_enabled; 4675330217Seadler boolean_t tlv_lar = fw_has_capa(&sc->ucode_capa, 4676330217Seadler IWM_UCODE_TLV_CAPA_LAR_SUPPORT); 4677330217Seadler 4678330217Seadler if (iwm_lar_disable) 4679330217Seadler return FALSE; 4680330217Seadler 4681330217Seadler /* 4682330217Seadler * Enable LAR only if it is supported by the FW (TLV) && 4683330217Seadler * enabled in the NVM 4684330217Seadler */ 4685330217Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 4686330217Seadler return nvm_lar && tlv_lar; 4687330217Seadler else 4688330217Seadler return tlv_lar; 4689330217Seadler} 4690330217Seadler 4691330217Seadlerstatic boolean_t 4692330217Seadleriwm_mvm_is_wifi_mcc_supported(struct iwm_softc *sc) 4693330217Seadler{ 4694330217Seadler return fw_has_api(&sc->ucode_capa, 4695330217Seadler IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || 4696330217Seadler fw_has_capa(&sc->ucode_capa, 4697330217Seadler IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC); 4698330217Seadler} 4699330217Seadler 4700303628Ssbrunostatic int 4701303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) 4702303628Ssbruno{ 4703303628Ssbruno struct iwm_mcc_update_cmd mcc_cmd; 4704303628Ssbruno struct iwm_host_cmd hcmd = { 4705303628Ssbruno .id = IWM_MCC_UPDATE_CMD, 4706303628Ssbruno .flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB), 4707303628Ssbruno .data = { &mcc_cmd }, 4708303628Ssbruno }; 4709303628Ssbruno int ret; 4710303628Ssbruno#ifdef IWM_DEBUG 4711303628Ssbruno struct iwm_rx_packet *pkt; 4712303628Ssbruno struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL; 4713303628Ssbruno struct iwm_mcc_update_resp *mcc_resp; 4714303628Ssbruno int n_channels; 4715303628Ssbruno uint16_t mcc; 4716303628Ssbruno#endif 4717330196Seadler int resp_v2 = fw_has_capa(&sc->ucode_capa, 4718303628Ssbruno IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); 4719303628Ssbruno 4720330217Seadler if (!iwm_mvm_is_lar_supported(sc)) { 4721330217Seadler IWM_DPRINTF(sc, IWM_DEBUG_LAR, "%s: no LAR support\n", 4722330217Seadler __func__); 4723330217Seadler return 0; 4724330217Seadler } 4725330217Seadler 4726303628Ssbruno memset(&mcc_cmd, 0, sizeof(mcc_cmd)); 4727303628Ssbruno mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]); 4728330217Seadler if (iwm_mvm_is_wifi_mcc_supported(sc)) 4729303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT; 4730303628Ssbruno else 4731303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW; 4732303628Ssbruno 4733303628Ssbruno if (resp_v2) 4734303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd); 4735303628Ssbruno else 4736303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1); 4737303628Ssbruno 4738330217Seadler IWM_DPRINTF(sc, IWM_DEBUG_LAR, 4739303628Ssbruno "send MCC update to FW with '%c%c' src = %d\n", 4740303628Ssbruno alpha2[0], alpha2[1], mcc_cmd.source_id); 4741303628Ssbruno 4742303628Ssbruno ret = iwm_send_cmd(sc, &hcmd); 4743303628Ssbruno if (ret) 4744303628Ssbruno return ret; 4745303628Ssbruno 4746303628Ssbruno#ifdef IWM_DEBUG 4747303628Ssbruno pkt = hcmd.resp_pkt; 4748303628Ssbruno 4749303628Ssbruno /* Extract MCC response */ 4750303628Ssbruno if (resp_v2) { 4751303628Ssbruno mcc_resp = (void *)pkt->data; 4752303628Ssbruno mcc = mcc_resp->mcc; 4753303628Ssbruno n_channels = le32toh(mcc_resp->n_channels); 4754303628Ssbruno } else { 4755303628Ssbruno mcc_resp_v1 = (void *)pkt->data; 4756303628Ssbruno mcc = mcc_resp_v1->mcc; 4757303628Ssbruno n_channels = le32toh(mcc_resp_v1->n_channels); 4758303628Ssbruno } 4759303628Ssbruno 4760303628Ssbruno /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ 4761303628Ssbruno if (mcc == 0) 4762303628Ssbruno mcc = 0x3030; /* "00" - world */ 4763303628Ssbruno 4764330217Seadler IWM_DPRINTF(sc, IWM_DEBUG_LAR, 4765303628Ssbruno "regulatory domain '%c%c' (%d channels available)\n", 4766303628Ssbruno mcc >> 8, mcc & 0xff, n_channels); 4767303628Ssbruno#endif 4768303628Ssbruno iwm_free_resp(sc, &hcmd); 4769303628Ssbruno 4770303628Ssbruno return 0; 4771303628Ssbruno} 4772303628Ssbruno 4773303628Ssbrunostatic void 4774303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff) 4775303628Ssbruno{ 4776303628Ssbruno struct iwm_host_cmd cmd = { 4777303628Ssbruno .id = IWM_REPLY_THERMAL_MNG_BACKOFF, 4778303628Ssbruno .len = { sizeof(uint32_t), }, 4779303628Ssbruno .data = { &backoff, }, 4780303628Ssbruno }; 4781303628Ssbruno 4782303628Ssbruno if (iwm_send_cmd(sc, &cmd) != 0) { 4783303628Ssbruno device_printf(sc->sc_dev, 4784303628Ssbruno "failed to change thermal tx backoff\n"); 4785303628Ssbruno } 4786303628Ssbruno} 4787303628Ssbruno 4788303628Ssbrunostatic int 4789286441Srpauloiwm_init_hw(struct iwm_softc *sc) 4790286441Srpaulo{ 4791287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4792303628Ssbruno int error, i, ac; 4793286441Srpaulo 4794303628Ssbruno if ((error = iwm_start_hw(sc)) != 0) { 4795303628Ssbruno printf("iwm_start_hw: failed %d\n", error); 4796286441Srpaulo return error; 4797303628Ssbruno } 4798286441Srpaulo 4799286441Srpaulo if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) { 4800303628Ssbruno printf("iwm_run_init_mvm_ucode: failed %d\n", error); 4801286441Srpaulo return error; 4802286441Srpaulo } 4803286441Srpaulo 4804286441Srpaulo /* 4805286441Srpaulo * should stop and start HW since that INIT 4806286441Srpaulo * image just loaded 4807286441Srpaulo */ 4808286441Srpaulo iwm_stop_device(sc); 4809330201Seadler sc->sc_ps_disabled = FALSE; 4810286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 4811286441Srpaulo device_printf(sc->sc_dev, "could not initialize hardware\n"); 4812286441Srpaulo return error; 4813286441Srpaulo } 4814286441Srpaulo 4815286441Srpaulo /* omstart, this time with the regular firmware */ 4816330168Seadler error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_REGULAR); 4817286441Srpaulo if (error) { 4818286441Srpaulo device_printf(sc->sc_dev, "could not load firmware\n"); 4819286441Srpaulo goto error; 4820286441Srpaulo } 4821286441Srpaulo 4822303628Ssbruno if ((error = iwm_send_bt_init_conf(sc)) != 0) { 4823303628Ssbruno device_printf(sc->sc_dev, "bt init conf failed\n"); 4824286441Srpaulo goto error; 4825303628Ssbruno } 4826286441Srpaulo 4827330167Seadler error = iwm_send_tx_ant_cfg(sc, iwm_mvm_get_valid_tx_ant(sc)); 4828330167Seadler if (error != 0) { 4829303628Ssbruno device_printf(sc->sc_dev, "antenna config failed\n"); 4830303628Ssbruno goto error; 4831303628Ssbruno } 4832303628Ssbruno 4833330163Seadler /* Send phy db control command and then phy db calibration */ 4834330163Seadler if ((error = iwm_send_phy_db_data(sc->sc_phy_db)) != 0) 4835286441Srpaulo goto error; 4836286441Srpaulo 4837303628Ssbruno if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) { 4838303628Ssbruno device_printf(sc->sc_dev, "phy_cfg_cmd failed\n"); 4839286441Srpaulo goto error; 4840303628Ssbruno } 4841286441Srpaulo 4842286441Srpaulo /* Add auxiliary station for scanning */ 4843303628Ssbruno if ((error = iwm_mvm_add_aux_sta(sc)) != 0) { 4844303628Ssbruno device_printf(sc->sc_dev, "add_aux_sta failed\n"); 4845286441Srpaulo goto error; 4846303628Ssbruno } 4847286441Srpaulo 4848286441Srpaulo for (i = 0; i < IWM_NUM_PHY_CTX; i++) { 4849286441Srpaulo /* 4850286441Srpaulo * The channel used here isn't relevant as it's 4851286441Srpaulo * going to be overwritten in the other flows. 4852286441Srpaulo * For now use the first channel we have. 4853286441Srpaulo */ 4854286441Srpaulo if ((error = iwm_mvm_phy_ctxt_add(sc, 4855286441Srpaulo &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0) 4856286441Srpaulo goto error; 4857286441Srpaulo } 4858286441Srpaulo 4859303628Ssbruno /* Initialize tx backoffs to the minimum. */ 4860330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 4861303628Ssbruno iwm_mvm_tt_tx_backoff(sc, 0); 4862303628Ssbruno 4863286441Srpaulo error = iwm_mvm_power_update_device(sc); 4864286441Srpaulo if (error) 4865286441Srpaulo goto error; 4866286441Srpaulo 4867330217Seadler if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) 4868330217Seadler goto error; 4869286441Srpaulo 4870330196Seadler if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { 4871303628Ssbruno if ((error = iwm_mvm_config_umac_scan(sc)) != 0) 4872303628Ssbruno goto error; 4873303628Ssbruno } 4874303628Ssbruno 4875303628Ssbruno /* Enable Tx queues. */ 4876303628Ssbruno for (ac = 0; ac < WME_NUM_AC; ac++) { 4877303628Ssbruno error = iwm_enable_txq(sc, IWM_STATION_ID, ac, 4878303628Ssbruno iwm_mvm_ac_to_tx_fifo[ac]); 4879303628Ssbruno if (error) 4880303628Ssbruno goto error; 4881303628Ssbruno } 4882303628Ssbruno 4883303628Ssbruno if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) { 4884303628Ssbruno device_printf(sc->sc_dev, "failed to disable beacon filter\n"); 4885303628Ssbruno goto error; 4886303628Ssbruno } 4887303628Ssbruno 4888286441Srpaulo return 0; 4889286441Srpaulo 4890286441Srpaulo error: 4891286441Srpaulo iwm_stop_device(sc); 4892286441Srpaulo return error; 4893286441Srpaulo} 4894286441Srpaulo 4895286441Srpaulo/* Allow multicast from our BSSID. */ 4896286441Srpaulostatic int 4897286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc) 4898286441Srpaulo{ 4899286441Srpaulo struct ieee80211_node *ni = vap->iv_bss; 4900286441Srpaulo struct iwm_mcast_filter_cmd *cmd; 4901286441Srpaulo size_t size; 4902286441Srpaulo int error; 4903286441Srpaulo 4904286441Srpaulo size = roundup(sizeof(*cmd), 4); 4905286441Srpaulo cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); 4906286441Srpaulo if (cmd == NULL) 4907286441Srpaulo return ENOMEM; 4908286441Srpaulo cmd->filter_own = 1; 4909286441Srpaulo cmd->port_id = 0; 4910286441Srpaulo cmd->count = 0; 4911286441Srpaulo cmd->pass_all = 1; 4912286441Srpaulo IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); 4913286441Srpaulo 4914286441Srpaulo error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 4915286441Srpaulo IWM_CMD_SYNC, size, cmd); 4916286441Srpaulo free(cmd, M_DEVBUF); 4917286441Srpaulo 4918286441Srpaulo return (error); 4919286441Srpaulo} 4920286441Srpaulo 4921303628Ssbruno/* 4922303628Ssbruno * ifnet interfaces 4923303628Ssbruno */ 4924303628Ssbruno 4925286441Srpaulostatic void 4926287197Sglebiusiwm_init(struct iwm_softc *sc) 4927286441Srpaulo{ 4928286441Srpaulo int error; 4929286441Srpaulo 4930286441Srpaulo if (sc->sc_flags & IWM_FLAG_HW_INITED) { 4931286441Srpaulo return; 4932286441Srpaulo } 4933286441Srpaulo sc->sc_generation++; 4934286441Srpaulo sc->sc_flags &= ~IWM_FLAG_STOPPED; 4935286441Srpaulo 4936286441Srpaulo if ((error = iwm_init_hw(sc)) != 0) { 4937303628Ssbruno printf("iwm_init_hw failed %d\n", error); 4938287197Sglebius iwm_stop(sc); 4939286441Srpaulo return; 4940286441Srpaulo } 4941286441Srpaulo 4942286441Srpaulo /* 4943303628Ssbruno * Ok, firmware loaded and we are jogging 4944286441Srpaulo */ 4945286441Srpaulo sc->sc_flags |= IWM_FLAG_HW_INITED; 4946286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 4947286441Srpaulo} 4948286441Srpaulo 4949287197Sglebiusstatic int 4950287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m) 4951286441Srpaulo{ 4952287197Sglebius struct iwm_softc *sc; 4953287197Sglebius int error; 4954286441Srpaulo 4955287197Sglebius sc = ic->ic_softc; 4956287197Sglebius 4957286441Srpaulo IWM_LOCK(sc); 4958287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 4959287197Sglebius IWM_UNLOCK(sc); 4960287197Sglebius return (ENXIO); 4961287197Sglebius } 4962287197Sglebius error = mbufq_enqueue(&sc->sc_snd, m); 4963287197Sglebius if (error) { 4964287197Sglebius IWM_UNLOCK(sc); 4965287197Sglebius return (error); 4966287197Sglebius } 4967287197Sglebius iwm_start(sc); 4968286441Srpaulo IWM_UNLOCK(sc); 4969287197Sglebius return (0); 4970286441Srpaulo} 4971286441Srpaulo 4972287197Sglebius/* 4973287197Sglebius * Dequeue packets from sendq and call send. 4974287197Sglebius */ 4975286441Srpaulostatic void 4976287197Sglebiusiwm_start(struct iwm_softc *sc) 4977286441Srpaulo{ 4978286441Srpaulo struct ieee80211_node *ni; 4979286441Srpaulo struct mbuf *m; 4980286441Srpaulo int ac = 0; 4981286441Srpaulo 4982286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__); 4983287197Sglebius while (sc->qfullmsk == 0 && 4984287197Sglebius (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 4985286441Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 4986286441Srpaulo if (iwm_tx(sc, m, ni, ac) != 0) { 4987287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 4988287197Sglebius IFCOUNTER_OERRORS, 1); 4989286441Srpaulo ieee80211_free_node(ni); 4990286441Srpaulo continue; 4991286441Srpaulo } 4992287197Sglebius sc->sc_tx_timer = 15; 4993286441Srpaulo } 4994286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); 4995286441Srpaulo} 4996286441Srpaulo 4997286441Srpaulostatic void 4998287197Sglebiusiwm_stop(struct iwm_softc *sc) 4999286441Srpaulo{ 5000286441Srpaulo 5001286441Srpaulo sc->sc_flags &= ~IWM_FLAG_HW_INITED; 5002286441Srpaulo sc->sc_flags |= IWM_FLAG_STOPPED; 5003286441Srpaulo sc->sc_generation++; 5004301187Sadrian iwm_led_blink_stop(sc); 5005286441Srpaulo sc->sc_tx_timer = 0; 5006286441Srpaulo iwm_stop_device(sc); 5007330174Seadler sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING; 5008286441Srpaulo} 5009286441Srpaulo 5010286441Srpaulostatic void 5011286441Srpauloiwm_watchdog(void *arg) 5012286441Srpaulo{ 5013286441Srpaulo struct iwm_softc *sc = arg; 5014300242Savos struct ieee80211com *ic = &sc->sc_ic; 5015286441Srpaulo 5016286441Srpaulo if (sc->sc_tx_timer > 0) { 5017286441Srpaulo if (--sc->sc_tx_timer == 0) { 5018286441Srpaulo device_printf(sc->sc_dev, "device timeout\n"); 5019286441Srpaulo#ifdef IWM_DEBUG 5020286441Srpaulo iwm_nic_error(sc); 5021286441Srpaulo#endif 5022300242Savos ieee80211_restart_all(ic); 5023303628Ssbruno counter_u64_add(sc->sc_ic.ic_oerrors, 1); 5024286441Srpaulo return; 5025286441Srpaulo } 5026286441Srpaulo } 5027286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 5028286441Srpaulo} 5029286441Srpaulo 5030287197Sglebiusstatic void 5031287197Sglebiusiwm_parent(struct ieee80211com *ic) 5032286441Srpaulo{ 5033287197Sglebius struct iwm_softc *sc = ic->ic_softc; 5034287197Sglebius int startall = 0; 5035286441Srpaulo 5036287197Sglebius IWM_LOCK(sc); 5037287197Sglebius if (ic->ic_nrunning > 0) { 5038287197Sglebius if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) { 5039287197Sglebius iwm_init(sc); 5040287197Sglebius startall = 1; 5041286441Srpaulo } 5042287197Sglebius } else if (sc->sc_flags & IWM_FLAG_HW_INITED) 5043287197Sglebius iwm_stop(sc); 5044287197Sglebius IWM_UNLOCK(sc); 5045287197Sglebius if (startall) 5046287197Sglebius ieee80211_start_all(ic); 5047286441Srpaulo} 5048286441Srpaulo 5049286441Srpaulo/* 5050286441Srpaulo * The interrupt side of things 5051286441Srpaulo */ 5052286441Srpaulo 5053286441Srpaulo/* 5054286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c 5055286441Srpaulo */ 5056286441Srpaulo 5057286441Srpaulo/* 5058286441Srpaulo * Note: This structure is read from the device with IO accesses, 5059286441Srpaulo * and the reading already does the endian conversion. As it is 5060286441Srpaulo * read with uint32_t-sized accesses, any members with a different size 5061286441Srpaulo * need to be ordered correctly though! 5062286441Srpaulo */ 5063286441Srpaulostruct iwm_error_event_table { 5064286441Srpaulo uint32_t valid; /* (nonzero) valid, (0) log is empty */ 5065286441Srpaulo uint32_t error_id; /* type of error */ 5066303628Ssbruno uint32_t trm_hw_status0; /* TRM HW status */ 5067303628Ssbruno uint32_t trm_hw_status1; /* TRM HW status */ 5068286441Srpaulo uint32_t blink2; /* branch link */ 5069286441Srpaulo uint32_t ilink1; /* interrupt link */ 5070286441Srpaulo uint32_t ilink2; /* interrupt link */ 5071286441Srpaulo uint32_t data1; /* error-specific data */ 5072286441Srpaulo uint32_t data2; /* error-specific data */ 5073286441Srpaulo uint32_t data3; /* error-specific data */ 5074286441Srpaulo uint32_t bcon_time; /* beacon timer */ 5075286441Srpaulo uint32_t tsf_low; /* network timestamp function timer */ 5076286441Srpaulo uint32_t tsf_hi; /* network timestamp function timer */ 5077286441Srpaulo uint32_t gp1; /* GP1 timer register */ 5078286441Srpaulo uint32_t gp2; /* GP2 timer register */ 5079303628Ssbruno uint32_t fw_rev_type; /* firmware revision type */ 5080303628Ssbruno uint32_t major; /* uCode version major */ 5081303628Ssbruno uint32_t minor; /* uCode version minor */ 5082286441Srpaulo uint32_t hw_ver; /* HW Silicon version */ 5083286441Srpaulo uint32_t brd_ver; /* HW board version */ 5084286441Srpaulo uint32_t log_pc; /* log program counter */ 5085286441Srpaulo uint32_t frame_ptr; /* frame pointer */ 5086286441Srpaulo uint32_t stack_ptr; /* stack pointer */ 5087286441Srpaulo uint32_t hcmd; /* last host command header */ 5088286441Srpaulo uint32_t isr0; /* isr status register LMPM_NIC_ISR0: 5089286441Srpaulo * rxtx_flag */ 5090286441Srpaulo uint32_t isr1; /* isr status register LMPM_NIC_ISR1: 5091286441Srpaulo * host_flag */ 5092286441Srpaulo uint32_t isr2; /* isr status register LMPM_NIC_ISR2: 5093286441Srpaulo * enc_flag */ 5094286441Srpaulo uint32_t isr3; /* isr status register LMPM_NIC_ISR3: 5095286441Srpaulo * time_flag */ 5096286441Srpaulo uint32_t isr4; /* isr status register LMPM_NIC_ISR4: 5097286441Srpaulo * wico interrupt */ 5098303628Ssbruno uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ 5099286441Srpaulo uint32_t wait_event; /* wait event() caller address */ 5100286441Srpaulo uint32_t l2p_control; /* L2pControlField */ 5101286441Srpaulo uint32_t l2p_duration; /* L2pDurationField */ 5102286441Srpaulo uint32_t l2p_mhvalid; /* L2pMhValidBits */ 5103286441Srpaulo uint32_t l2p_addr_match; /* L2pAddrMatchStat */ 5104286441Srpaulo uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on 5105286441Srpaulo * (LMPM_PMG_SEL) */ 5106286441Srpaulo uint32_t u_timestamp; /* indicate when the date and time of the 5107286441Srpaulo * compilation */ 5108286441Srpaulo uint32_t flow_handler; /* FH read/write pointers, RX credit */ 5109303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; 5110303628Ssbruno 5111303628Ssbruno/* 5112303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip. 5113303628Ssbruno * Note: This structure is read from the device with IO accesses, 5114303628Ssbruno * and the reading already does the endian conversion. As it is 5115303628Ssbruno * read with u32-sized accesses, any members with a different size 5116303628Ssbruno * need to be ordered correctly though! 5117303628Ssbruno */ 5118303628Ssbrunostruct iwm_umac_error_event_table { 5119303628Ssbruno uint32_t valid; /* (nonzero) valid, (0) log is empty */ 5120303628Ssbruno uint32_t error_id; /* type of error */ 5121303628Ssbruno uint32_t blink1; /* branch link */ 5122303628Ssbruno uint32_t blink2; /* branch link */ 5123303628Ssbruno uint32_t ilink1; /* interrupt link */ 5124303628Ssbruno uint32_t ilink2; /* interrupt link */ 5125303628Ssbruno uint32_t data1; /* error-specific data */ 5126303628Ssbruno uint32_t data2; /* error-specific data */ 5127303628Ssbruno uint32_t data3; /* error-specific data */ 5128303628Ssbruno uint32_t umac_major; 5129303628Ssbruno uint32_t umac_minor; 5130303628Ssbruno uint32_t frame_pointer; /* core register 27*/ 5131303628Ssbruno uint32_t stack_pointer; /* core register 28 */ 5132303628Ssbruno uint32_t cmd_header; /* latest host cmd sent to UMAC */ 5133303628Ssbruno uint32_t nic_isr_pref; /* ISR status register */ 5134286441Srpaulo} __packed; 5135286441Srpaulo 5136286441Srpaulo#define ERROR_START_OFFSET (1 * sizeof(uint32_t)) 5137286441Srpaulo#define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) 5138286441Srpaulo 5139286441Srpaulo#ifdef IWM_DEBUG 5140286441Srpaulostruct { 5141286441Srpaulo const char *name; 5142286441Srpaulo uint8_t num; 5143286441Srpaulo} advanced_lookup[] = { 5144286441Srpaulo { "NMI_INTERRUPT_WDG", 0x34 }, 5145286441Srpaulo { "SYSASSERT", 0x35 }, 5146286441Srpaulo { "UCODE_VERSION_MISMATCH", 0x37 }, 5147286441Srpaulo { "BAD_COMMAND", 0x38 }, 5148286441Srpaulo { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, 5149286441Srpaulo { "FATAL_ERROR", 0x3D }, 5150286441Srpaulo { "NMI_TRM_HW_ERR", 0x46 }, 5151286441Srpaulo { "NMI_INTERRUPT_TRM", 0x4C }, 5152286441Srpaulo { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, 5153286441Srpaulo { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, 5154286441Srpaulo { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, 5155286441Srpaulo { "NMI_INTERRUPT_HOST", 0x66 }, 5156286441Srpaulo { "NMI_INTERRUPT_ACTION_PT", 0x7C }, 5157286441Srpaulo { "NMI_INTERRUPT_UNKNOWN", 0x84 }, 5158286441Srpaulo { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, 5159286441Srpaulo { "ADVANCED_SYSASSERT", 0 }, 5160286441Srpaulo}; 5161286441Srpaulo 5162286441Srpaulostatic const char * 5163286441Srpauloiwm_desc_lookup(uint32_t num) 5164286441Srpaulo{ 5165286441Srpaulo int i; 5166286441Srpaulo 5167286441Srpaulo for (i = 0; i < nitems(advanced_lookup) - 1; i++) 5168286441Srpaulo if (advanced_lookup[i].num == num) 5169286441Srpaulo return advanced_lookup[i].name; 5170286441Srpaulo 5171286441Srpaulo /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ 5172286441Srpaulo return advanced_lookup[i].name; 5173286441Srpaulo} 5174286441Srpaulo 5175303628Ssbrunostatic void 5176303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc) 5177303628Ssbruno{ 5178303628Ssbruno struct iwm_umac_error_event_table table; 5179303628Ssbruno uint32_t base; 5180303628Ssbruno 5181330183Seadler base = sc->umac_error_event_table; 5182303628Ssbruno 5183303628Ssbruno if (base < 0x800000) { 5184303628Ssbruno device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", 5185303628Ssbruno base); 5186303628Ssbruno return; 5187303628Ssbruno } 5188303628Ssbruno 5189303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 5190303628Ssbruno device_printf(sc->sc_dev, "reading errlog failed\n"); 5191303628Ssbruno return; 5192303628Ssbruno } 5193303628Ssbruno 5194303628Ssbruno if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 5195303628Ssbruno device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n"); 5196303628Ssbruno device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 5197303628Ssbruno sc->sc_flags, table.valid); 5198303628Ssbruno } 5199303628Ssbruno 5200303628Ssbruno device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id, 5201303628Ssbruno iwm_desc_lookup(table.error_id)); 5202303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1); 5203303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2); 5204303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n", 5205303628Ssbruno table.ilink1); 5206303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n", 5207303628Ssbruno table.ilink2); 5208303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1); 5209303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2); 5210303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3); 5211303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major); 5212303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor); 5213303628Ssbruno device_printf(sc->sc_dev, "0x%08X | frame pointer\n", 5214303628Ssbruno table.frame_pointer); 5215303628Ssbruno device_printf(sc->sc_dev, "0x%08X | stack pointer\n", 5216303628Ssbruno table.stack_pointer); 5217303628Ssbruno device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header); 5218303628Ssbruno device_printf(sc->sc_dev, "0x%08X | isr status reg\n", 5219303628Ssbruno table.nic_isr_pref); 5220303628Ssbruno} 5221303628Ssbruno 5222286441Srpaulo/* 5223286441Srpaulo * Support for dumping the error log seemed like a good idea ... 5224286441Srpaulo * but it's mostly hex junk and the only sensible thing is the 5225286441Srpaulo * hw/ucode revision (which we know anyway). Since it's here, 5226286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to 5227286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later. 5228286441Srpaulo */ 5229286441Srpaulostatic void 5230286441Srpauloiwm_nic_error(struct iwm_softc *sc) 5231286441Srpaulo{ 5232286441Srpaulo struct iwm_error_event_table table; 5233286441Srpaulo uint32_t base; 5234286441Srpaulo 5235286441Srpaulo device_printf(sc->sc_dev, "dumping device error log\n"); 5236330186Seadler base = sc->error_event_table; 5237303628Ssbruno if (base < 0x800000) { 5238286441Srpaulo device_printf(sc->sc_dev, 5239303628Ssbruno "Invalid error log pointer 0x%08x\n", base); 5240286441Srpaulo return; 5241286441Srpaulo } 5242286441Srpaulo 5243303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 5244286441Srpaulo device_printf(sc->sc_dev, "reading errlog failed\n"); 5245286441Srpaulo return; 5246286441Srpaulo } 5247286441Srpaulo 5248286441Srpaulo if (!table.valid) { 5249286441Srpaulo device_printf(sc->sc_dev, "errlog not found, skipping\n"); 5250286441Srpaulo return; 5251286441Srpaulo } 5252286441Srpaulo 5253286441Srpaulo if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 5254303628Ssbruno device_printf(sc->sc_dev, "Start Error Log Dump:\n"); 5255286441Srpaulo device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 5256286441Srpaulo sc->sc_flags, table.valid); 5257286441Srpaulo } 5258286441Srpaulo 5259286441Srpaulo device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id, 5260303628Ssbruno iwm_desc_lookup(table.error_id)); 5261303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status0\n", 5262303628Ssbruno table.trm_hw_status0); 5263303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status1\n", 5264303628Ssbruno table.trm_hw_status1); 5265286441Srpaulo device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2); 5266286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1); 5267286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2); 5268286441Srpaulo device_printf(sc->sc_dev, "%08X | data1\n", table.data1); 5269286441Srpaulo device_printf(sc->sc_dev, "%08X | data2\n", table.data2); 5270286441Srpaulo device_printf(sc->sc_dev, "%08X | data3\n", table.data3); 5271286441Srpaulo device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time); 5272286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low); 5273286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi); 5274286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1); 5275286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2); 5276303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode revision type\n", 5277303628Ssbruno table.fw_rev_type); 5278303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major); 5279303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor); 5280286441Srpaulo device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver); 5281286441Srpaulo device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver); 5282286441Srpaulo device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd); 5283286441Srpaulo device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0); 5284286441Srpaulo device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1); 5285286441Srpaulo device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2); 5286286441Srpaulo device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3); 5287286441Srpaulo device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4); 5288303628Ssbruno device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id); 5289286441Srpaulo device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event); 5290286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control); 5291286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration); 5292286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid); 5293286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match); 5294286441Srpaulo device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); 5295286441Srpaulo device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); 5296286441Srpaulo device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); 5297303628Ssbruno 5298330183Seadler if (sc->umac_error_event_table) 5299303628Ssbruno iwm_nic_umac_error(sc); 5300286441Srpaulo} 5301286441Srpaulo#endif 5302286441Srpaulo 5303286441Srpaulostatic void 5304330208Seadleriwm_handle_rxb(struct iwm_softc *sc, struct mbuf *m) 5305286441Srpaulo{ 5306303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 5307330208Seadler struct iwm_cmd_response *cresp; 5308330208Seadler struct mbuf *m1; 5309330208Seadler uint32_t offset = 0; 5310330208Seadler uint32_t maxoff = IWM_RBUF_SIZE; 5311330208Seadler uint32_t nextoff; 5312330208Seadler boolean_t stolen = FALSE; 5313286441Srpaulo 5314330208Seadler#define HAVEROOM(a) \ 5315330208Seadler ((a) + sizeof(uint32_t) + sizeof(struct iwm_cmd_header) < maxoff) 5316286441Srpaulo 5317330208Seadler while (HAVEROOM(offset)) { 5318330208Seadler struct iwm_rx_packet *pkt = mtodoff(m, struct iwm_rx_packet *, 5319330208Seadler offset); 5320330208Seadler int qid, idx, code, len; 5321303628Ssbruno 5322330208Seadler qid = pkt->hdr.qid; 5323286441Srpaulo idx = pkt->hdr.idx; 5324286441Srpaulo 5325303628Ssbruno code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); 5326286441Srpaulo 5327286441Srpaulo /* 5328286441Srpaulo * randomly get these from the firmware, no idea why. 5329286441Srpaulo * they at least seem harmless, so just ignore them for now 5330286441Srpaulo */ 5331330208Seadler if ((pkt->hdr.code == 0 && (qid & ~0x80) == 0 && idx == 0) || 5332330208Seadler pkt->len_n_flags == htole32(IWM_FH_RSCSR_FRAME_INVALID)) { 5333330208Seadler break; 5334286441Srpaulo } 5335286441Srpaulo 5336330208Seadler IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5337330208Seadler "rx packet qid=%d idx=%d type=%x\n", 5338330208Seadler qid & ~0x80, pkt->hdr.idx, code); 5339330208Seadler 5340330220Seadler len = iwm_rx_packet_len(pkt); 5341330208Seadler len += sizeof(uint32_t); /* account for status word */ 5342330208Seadler nextoff = offset + roundup2(len, IWM_FH_RSCSR_FRAME_ALIGN); 5343330208Seadler 5344330170Seadler iwm_notification_wait_notify(sc->sc_notif_wait, code, pkt); 5345330170Seadler 5346303628Ssbruno switch (code) { 5347286441Srpaulo case IWM_REPLY_RX_PHY_CMD: 5348330208Seadler iwm_mvm_rx_rx_phy_cmd(sc, pkt); 5349286441Srpaulo break; 5350286441Srpaulo 5351330208Seadler case IWM_REPLY_RX_MPDU_CMD: { 5352330208Seadler /* 5353330208Seadler * If this is the last frame in the RX buffer, we 5354330208Seadler * can directly feed the mbuf to the sharks here. 5355330208Seadler */ 5356330208Seadler struct iwm_rx_packet *nextpkt = mtodoff(m, 5357330208Seadler struct iwm_rx_packet *, nextoff); 5358330208Seadler if (!HAVEROOM(nextoff) || 5359330208Seadler (nextpkt->hdr.code == 0 && 5360330208Seadler (nextpkt->hdr.qid & ~0x80) == 0 && 5361330208Seadler nextpkt->hdr.idx == 0) || 5362330208Seadler (nextpkt->len_n_flags == 5363330208Seadler htole32(IWM_FH_RSCSR_FRAME_INVALID))) { 5364330208Seadler if (iwm_mvm_rx_rx_mpdu(sc, m, offset, stolen)) { 5365330208Seadler stolen = FALSE; 5366330208Seadler /* Make sure we abort the loop */ 5367330208Seadler nextoff = maxoff; 5368330208Seadler } 5369330208Seadler break; 5370330208Seadler } 5371330208Seadler 5372330208Seadler /* 5373330208Seadler * Use m_copym instead of m_split, because that 5374330208Seadler * makes it easier to keep a valid rx buffer in 5375330208Seadler * the ring, when iwm_mvm_rx_rx_mpdu() fails. 5376330208Seadler * 5377330208Seadler * We need to start m_copym() at offset 0, to get the 5378330208Seadler * M_PKTHDR flag preserved. 5379330208Seadler */ 5380330208Seadler m1 = m_copym(m, 0, M_COPYALL, M_NOWAIT); 5381330208Seadler if (m1) { 5382330208Seadler if (iwm_mvm_rx_rx_mpdu(sc, m1, offset, stolen)) 5383330208Seadler stolen = TRUE; 5384330208Seadler else 5385330208Seadler m_freem(m1); 5386330208Seadler } 5387286441Srpaulo break; 5388330208Seadler } 5389286441Srpaulo 5390286441Srpaulo case IWM_TX_CMD: 5391330208Seadler iwm_mvm_rx_tx_cmd(sc, pkt); 5392286441Srpaulo break; 5393286441Srpaulo 5394286441Srpaulo case IWM_MISSED_BEACONS_NOTIFICATION: { 5395286441Srpaulo struct iwm_missed_beacons_notif *resp; 5396286441Srpaulo int missed; 5397286441Srpaulo 5398286441Srpaulo /* XXX look at mac_id to determine interface ID */ 5399286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5400286441Srpaulo 5401330158Seadler resp = (void *)pkt->data; 5402286441Srpaulo missed = le32toh(resp->consec_missed_beacons); 5403286441Srpaulo 5404286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE, 5405286441Srpaulo "%s: MISSED_BEACON: mac_id=%d, " 5406286441Srpaulo "consec_since_last_rx=%d, consec=%d, num_expect=%d " 5407286441Srpaulo "num_rx=%d\n", 5408286441Srpaulo __func__, 5409286441Srpaulo le32toh(resp->mac_id), 5410286441Srpaulo le32toh(resp->consec_missed_beacons_since_last_rx), 5411286441Srpaulo le32toh(resp->consec_missed_beacons), 5412286441Srpaulo le32toh(resp->num_expected_beacons), 5413286441Srpaulo le32toh(resp->num_recvd_beacons)); 5414286441Srpaulo 5415286441Srpaulo /* Be paranoid */ 5416286441Srpaulo if (vap == NULL) 5417286441Srpaulo break; 5418286441Srpaulo 5419286441Srpaulo /* XXX no net80211 locking? */ 5420286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && 5421286441Srpaulo (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 5422286441Srpaulo if (missed > vap->iv_bmissthreshold) { 5423286441Srpaulo /* XXX bad locking; turn into task */ 5424286441Srpaulo IWM_UNLOCK(sc); 5425286441Srpaulo ieee80211_beacon_miss(ic); 5426286441Srpaulo IWM_LOCK(sc); 5427286441Srpaulo } 5428286441Srpaulo } 5429286441Srpaulo 5430330193Seadler break; 5431330193Seadler } 5432286441Srpaulo 5433303628Ssbruno case IWM_MFUART_LOAD_NOTIFICATION: 5434303628Ssbruno break; 5435303628Ssbruno 5436330183Seadler case IWM_MVM_ALIVE: 5437330183Seadler break; 5438286441Srpaulo 5439330169Seadler case IWM_CALIB_RES_NOTIF_PHY_DB: 5440330169Seadler break; 5441286441Srpaulo 5442330224Seadler case IWM_STATISTICS_NOTIFICATION: 5443330224Seadler iwm_mvm_handle_rx_statistics(sc, pkt); 5444330193Seadler break; 5445286441Srpaulo 5446286441Srpaulo case IWM_NVM_ACCESS_CMD: 5447303628Ssbruno case IWM_MCC_UPDATE_CMD: 5448330208Seadler if (sc->sc_wantresp == (((qid & ~0x80) << 16) | idx)) { 5449286441Srpaulo memcpy(sc->sc_cmd_resp, 5450286441Srpaulo pkt, sizeof(sc->sc_cmd_resp)); 5451286441Srpaulo } 5452286441Srpaulo break; 5453286441Srpaulo 5454303628Ssbruno case IWM_MCC_CHUB_UPDATE_CMD: { 5455303628Ssbruno struct iwm_mcc_chub_notif *notif; 5456330158Seadler notif = (void *)pkt->data; 5457303628Ssbruno 5458303628Ssbruno sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; 5459303628Ssbruno sc->sc_fw_mcc[1] = notif->mcc & 0xff; 5460303628Ssbruno sc->sc_fw_mcc[2] = '\0'; 5461330217Seadler IWM_DPRINTF(sc, IWM_DEBUG_LAR, 5462303628Ssbruno "fw source %d sent CC '%s'\n", 5463303628Ssbruno notif->source_id, sc->sc_fw_mcc); 5464330193Seadler break; 5465330193Seadler } 5466303628Ssbruno 5467330178Seadler case IWM_DTS_MEASUREMENT_NOTIFICATION: 5468330178Seadler case IWM_WIDE_ID(IWM_PHY_OPS_GROUP, 5469330178Seadler IWM_DTS_MEASUREMENT_NOTIF_WIDE): { 5470330177Seadler struct iwm_dts_measurement_notif_v1 *notif; 5471330177Seadler 5472330177Seadler if (iwm_rx_packet_payload_len(pkt) < sizeof(*notif)) { 5473330177Seadler device_printf(sc->sc_dev, 5474330177Seadler "Invalid DTS_MEASUREMENT_NOTIFICATION\n"); 5475330177Seadler break; 5476330177Seadler } 5477330177Seadler notif = (void *)pkt->data; 5478330177Seadler IWM_DPRINTF(sc, IWM_DEBUG_TEMP, 5479330177Seadler "IWM_DTS_MEASUREMENT_NOTIFICATION - %d\n", 5480330177Seadler notif->temp); 5481303628Ssbruno break; 5482330177Seadler } 5483303628Ssbruno 5484286441Srpaulo case IWM_PHY_CONFIGURATION_CMD: 5485286441Srpaulo case IWM_TX_ANT_CONFIGURATION_CMD: 5486286441Srpaulo case IWM_ADD_STA: 5487286441Srpaulo case IWM_MAC_CONTEXT_CMD: 5488286441Srpaulo case IWM_REPLY_SF_CFG_CMD: 5489286441Srpaulo case IWM_POWER_TABLE_CMD: 5490286441Srpaulo case IWM_PHY_CONTEXT_CMD: 5491286441Srpaulo case IWM_BINDING_CONTEXT_CMD: 5492286441Srpaulo case IWM_TIME_EVENT_CMD: 5493303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): 5494303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): 5495330214Seadler case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_ABORT_UMAC): 5496303628Ssbruno case IWM_SCAN_OFFLOAD_REQUEST_CMD: 5497330173Seadler case IWM_SCAN_OFFLOAD_ABORT_CMD: 5498286441Srpaulo case IWM_REPLY_BEACON_FILTERING_CMD: 5499286441Srpaulo case IWM_MAC_PM_POWER_TABLE: 5500286441Srpaulo case IWM_TIME_QUOTA_CMD: 5501286441Srpaulo case IWM_REMOVE_STA: 5502286441Srpaulo case IWM_TXPATH_FLUSH: 5503286441Srpaulo case IWM_LQ_CMD: 5504330216Seadler case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, 5505330216Seadler IWM_FW_PAGING_BLOCK_CMD): 5506303628Ssbruno case IWM_BT_CONFIG: 5507303628Ssbruno case IWM_REPLY_THERMAL_MNG_BACKOFF: 5508330158Seadler cresp = (void *)pkt->data; 5509330208Seadler if (sc->sc_wantresp == (((qid & ~0x80) << 16) | idx)) { 5510286441Srpaulo memcpy(sc->sc_cmd_resp, 5511286441Srpaulo pkt, sizeof(*pkt)+sizeof(*cresp)); 5512286441Srpaulo } 5513286441Srpaulo break; 5514286441Srpaulo 5515286441Srpaulo /* ignore */ 5516330219Seadler case IWM_PHY_DB_CMD: 5517286441Srpaulo break; 5518286441Srpaulo 5519286441Srpaulo case IWM_INIT_COMPLETE_NOTIF: 5520286441Srpaulo break; 5521286441Srpaulo 5522303628Ssbruno case IWM_SCAN_OFFLOAD_COMPLETE: { 5523303628Ssbruno struct iwm_periodic_scan_complete *notif; 5524330158Seadler notif = (void *)pkt->data; 5525330174Seadler if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) { 5526330174Seadler sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING; 5527330174Seadler ieee80211_runtask(ic, &sc->sc_es_task); 5528330174Seadler } 5529303628Ssbruno break; 5530303628Ssbruno } 5531303628Ssbruno 5532303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE: { 5533303628Ssbruno struct iwm_lmac_scan_complete_notif *notif; 5534330158Seadler notif = (void *)pkt->data; 5535303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5536303628Ssbruno break; 5537303628Ssbruno } 5538303628Ssbruno 5539303628Ssbruno case IWM_SCAN_COMPLETE_UMAC: { 5540303628Ssbruno struct iwm_umac_scan_complete *notif; 5541330158Seadler notif = (void *)pkt->data; 5542303628Ssbruno 5543303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, 5544303628Ssbruno "UMAC scan complete, status=0x%x\n", 5545303628Ssbruno notif->status); 5546330174Seadler if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) { 5547330174Seadler sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING; 5548330174Seadler ieee80211_runtask(ic, &sc->sc_es_task); 5549330174Seadler } 5550303628Ssbruno break; 5551303628Ssbruno } 5552286441Srpaulo 5553303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE_UMAC: { 5554303628Ssbruno struct iwm_umac_scan_iter_complete_notif *notif; 5555330158Seadler notif = (void *)pkt->data; 5556303628Ssbruno 5557303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration " 5558303628Ssbruno "complete, status=0x%x, %d channels scanned\n", 5559303628Ssbruno notif->status, notif->scanned_channels); 5560303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5561303628Ssbruno break; 5562303628Ssbruno } 5563303628Ssbruno 5564286441Srpaulo case IWM_REPLY_ERROR: { 5565286441Srpaulo struct iwm_error_resp *resp; 5566330158Seadler resp = (void *)pkt->data; 5567286441Srpaulo 5568286441Srpaulo device_printf(sc->sc_dev, 5569286441Srpaulo "firmware error 0x%x, cmd 0x%x\n", 5570286441Srpaulo le32toh(resp->error_type), 5571286441Srpaulo resp->cmd_id); 5572303628Ssbruno break; 5573303628Ssbruno } 5574286441Srpaulo 5575286441Srpaulo case IWM_TIME_EVENT_NOTIFICATION: { 5576286441Srpaulo struct iwm_time_event_notif *notif; 5577330158Seadler notif = (void *)pkt->data; 5578286441Srpaulo 5579286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5580300833Sadrian "TE notif status = 0x%x action = 0x%x\n", 5581303628Ssbruno notif->status, notif->action); 5582303628Ssbruno break; 5583303628Ssbruno } 5584286441Srpaulo 5585330220Seadler /* 5586330220Seadler * Firmware versions 21 and 22 generate some DEBUG_LOG_MSG 5587330220Seadler * messages. Just ignore them for now. 5588330220Seadler */ 5589330220Seadler case IWM_DEBUG_LOG_MSG: 5590330220Seadler break; 5591330220Seadler 5592286441Srpaulo case IWM_MCAST_FILTER_CMD: 5593286441Srpaulo break; 5594286441Srpaulo 5595303628Ssbruno case IWM_SCD_QUEUE_CFG: { 5596303628Ssbruno struct iwm_scd_txq_cfg_rsp *rsp; 5597330158Seadler rsp = (void *)pkt->data; 5598303628Ssbruno 5599303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_CMD, 5600303628Ssbruno "queue cfg token=0x%x sta_id=%d " 5601303628Ssbruno "tid=%d scd_queue=%d\n", 5602303628Ssbruno rsp->token, rsp->sta_id, rsp->tid, 5603303628Ssbruno rsp->scd_queue); 5604303628Ssbruno break; 5605303628Ssbruno } 5606303628Ssbruno 5607286441Srpaulo default: 5608286441Srpaulo device_printf(sc->sc_dev, 5609286441Srpaulo "frame %d/%d %x UNHANDLED (this should " 5610330208Seadler "not happen)\n", qid & ~0x80, idx, 5611286441Srpaulo pkt->len_n_flags); 5612286441Srpaulo break; 5613286441Srpaulo } 5614286441Srpaulo 5615286441Srpaulo /* 5616286441Srpaulo * Why test bit 0x80? The Linux driver: 5617286441Srpaulo * 5618286441Srpaulo * There is one exception: uCode sets bit 15 when it 5619286441Srpaulo * originates the response/notification, i.e. when the 5620286441Srpaulo * response/notification is not a direct response to a 5621286441Srpaulo * command sent by the driver. For example, uCode issues 5622286441Srpaulo * IWM_REPLY_RX when it sends a received frame to the driver; 5623286441Srpaulo * it is not a direct response to any driver command. 5624286441Srpaulo * 5625286441Srpaulo * Ok, so since when is 7 == 15? Well, the Linux driver 5626286441Srpaulo * uses a slightly different format for pkt->hdr, and "qid" 5627286441Srpaulo * is actually the upper byte of a two-byte field. 5628286441Srpaulo */ 5629330208Seadler if (!(qid & (1 << 7))) 5630286441Srpaulo iwm_cmd_done(sc, pkt); 5631286441Srpaulo 5632330208Seadler offset = nextoff; 5633286441Srpaulo } 5634330208Seadler if (stolen) 5635330208Seadler m_freem(m); 5636330208Seadler#undef HAVEROOM 5637330208Seadler} 5638286441Srpaulo 5639330208Seadler/* 5640330208Seadler * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt. 5641330208Seadler * Basic structure from if_iwn 5642330208Seadler */ 5643330208Seadlerstatic void 5644330208Seadleriwm_notif_intr(struct iwm_softc *sc) 5645330208Seadler{ 5646330208Seadler uint16_t hw; 5647330208Seadler 5648330208Seadler bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 5649330208Seadler BUS_DMASYNC_POSTREAD); 5650330208Seadler 5651330208Seadler hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; 5652330208Seadler 5653286441Srpaulo /* 5654330208Seadler * Process responses 5655330208Seadler */ 5656330208Seadler while (sc->rxq.cur != hw) { 5657330208Seadler struct iwm_rx_ring *ring = &sc->rxq; 5658330208Seadler struct iwm_rx_data *data = &ring->data[ring->cur]; 5659330208Seadler 5660330208Seadler bus_dmamap_sync(ring->data_dmat, data->map, 5661330208Seadler BUS_DMASYNC_POSTREAD); 5662330208Seadler 5663330208Seadler IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5664330208Seadler "%s: hw = %d cur = %d\n", __func__, hw, ring->cur); 5665330208Seadler iwm_handle_rxb(sc, data->m); 5666330208Seadler 5667330208Seadler ring->cur = (ring->cur + 1) % IWM_RX_RING_COUNT; 5668330208Seadler } 5669330208Seadler 5670330208Seadler /* 5671330208Seadler * Tell the firmware that it can reuse the ring entries that 5672330208Seadler * we have just processed. 5673286441Srpaulo * Seems like the hardware gets upset unless we align 5674286441Srpaulo * the write by 8?? 5675286441Srpaulo */ 5676286441Srpaulo hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; 5677330208Seadler IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, rounddown2(hw, 8)); 5678286441Srpaulo} 5679286441Srpaulo 5680286441Srpaulostatic void 5681286441Srpauloiwm_intr(void *arg) 5682286441Srpaulo{ 5683286441Srpaulo struct iwm_softc *sc = arg; 5684286441Srpaulo int handled = 0; 5685286441Srpaulo int r1, r2, rv = 0; 5686286441Srpaulo int isperiodic = 0; 5687286441Srpaulo 5688286441Srpaulo IWM_LOCK(sc); 5689286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 5690286441Srpaulo 5691286441Srpaulo if (sc->sc_flags & IWM_FLAG_USE_ICT) { 5692286441Srpaulo uint32_t *ict = sc->ict_dma.vaddr; 5693286441Srpaulo int tmp; 5694286441Srpaulo 5695286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5696286441Srpaulo if (!tmp) 5697286441Srpaulo goto out_ena; 5698286441Srpaulo 5699286441Srpaulo /* 5700286441Srpaulo * ok, there was something. keep plowing until we have all. 5701286441Srpaulo */ 5702286441Srpaulo r1 = r2 = 0; 5703286441Srpaulo while (tmp) { 5704286441Srpaulo r1 |= tmp; 5705286441Srpaulo ict[sc->ict_cur] = 0; 5706286441Srpaulo sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT; 5707286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5708286441Srpaulo } 5709286441Srpaulo 5710286441Srpaulo /* this is where the fun begins. don't ask */ 5711286441Srpaulo if (r1 == 0xffffffff) 5712286441Srpaulo r1 = 0; 5713286441Srpaulo 5714286441Srpaulo /* i am not expected to understand this */ 5715286441Srpaulo if (r1 & 0xc0000) 5716286441Srpaulo r1 |= 0x8000; 5717286441Srpaulo r1 = (0xff & r1) | ((0xff00 & r1) << 16); 5718286441Srpaulo } else { 5719286441Srpaulo r1 = IWM_READ(sc, IWM_CSR_INT); 5720286441Srpaulo /* "hardware gone" (where, fishing?) */ 5721286441Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 5722286441Srpaulo goto out; 5723286441Srpaulo r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); 5724286441Srpaulo } 5725286441Srpaulo if (r1 == 0 && r2 == 0) { 5726286441Srpaulo goto out_ena; 5727286441Srpaulo } 5728286441Srpaulo 5729286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); 5730286441Srpaulo 5731330183Seadler /* Safely ignore these bits for debug checks below */ 5732330183Seadler r1 &= ~(IWM_CSR_INT_BIT_ALIVE | IWM_CSR_INT_BIT_SCD); 5733286441Srpaulo 5734286441Srpaulo if (r1 & IWM_CSR_INT_BIT_SW_ERR) { 5735286441Srpaulo int i; 5736287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5737286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5738286441Srpaulo 5739298793Sdchagin#ifdef IWM_DEBUG 5740286441Srpaulo iwm_nic_error(sc); 5741298793Sdchagin#endif 5742286441Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 5743286441Srpaulo device_printf(sc->sc_dev, "driver status:\n"); 5744286441Srpaulo for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) { 5745286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[i]; 5746286441Srpaulo device_printf(sc->sc_dev, 5747286441Srpaulo " tx ring %2d: qid=%-2d cur=%-3d " 5748286441Srpaulo "queued=%-3d\n", 5749286441Srpaulo i, ring->qid, ring->cur, ring->queued); 5750286441Srpaulo } 5751286441Srpaulo device_printf(sc->sc_dev, 5752286441Srpaulo " rx ring: cur=%d\n", sc->rxq.cur); 5753286441Srpaulo device_printf(sc->sc_dev, 5754298659Scem " 802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state); 5755286441Srpaulo 5756298594Sadrian /* Don't stop the device; just do a VAP restart */ 5757298594Sadrian IWM_UNLOCK(sc); 5758286441Srpaulo 5759298594Sadrian if (vap == NULL) { 5760298594Sadrian printf("%s: null vap\n", __func__); 5761298594Sadrian return; 5762298594Sadrian } 5763298594Sadrian 5764298594Sadrian device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " 5765298594Sadrian "restarting\n", __func__, vap->iv_state); 5766298594Sadrian 5767298594Sadrian ieee80211_restart_all(ic); 5768298594Sadrian return; 5769286441Srpaulo } 5770286441Srpaulo 5771286441Srpaulo if (r1 & IWM_CSR_INT_BIT_HW_ERR) { 5772286441Srpaulo handled |= IWM_CSR_INT_BIT_HW_ERR; 5773286441Srpaulo device_printf(sc->sc_dev, "hardware error, stopping device\n"); 5774287197Sglebius iwm_stop(sc); 5775286441Srpaulo rv = 1; 5776286441Srpaulo goto out; 5777286441Srpaulo } 5778286441Srpaulo 5779286441Srpaulo /* firmware chunk loaded */ 5780286441Srpaulo if (r1 & IWM_CSR_INT_BIT_FH_TX) { 5781286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); 5782286441Srpaulo handled |= IWM_CSR_INT_BIT_FH_TX; 5783286441Srpaulo sc->sc_fw_chunk_done = 1; 5784286441Srpaulo wakeup(&sc->sc_fw); 5785286441Srpaulo } 5786286441Srpaulo 5787286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RF_KILL) { 5788286441Srpaulo handled |= IWM_CSR_INT_BIT_RF_KILL; 5789287197Sglebius if (iwm_check_rfkill(sc)) { 5790286441Srpaulo device_printf(sc->sc_dev, 5791286441Srpaulo "%s: rfkill switch, disabling interface\n", 5792286441Srpaulo __func__); 5793287197Sglebius iwm_stop(sc); 5794286441Srpaulo } 5795286441Srpaulo } 5796286441Srpaulo 5797286441Srpaulo /* 5798286441Srpaulo * The Linux driver uses periodic interrupts to avoid races. 5799286441Srpaulo * We cargo-cult like it's going out of fashion. 5800286441Srpaulo */ 5801286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { 5802286441Srpaulo handled |= IWM_CSR_INT_BIT_RX_PERIODIC; 5803286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); 5804286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) 5805286441Srpaulo IWM_WRITE_1(sc, 5806286441Srpaulo IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); 5807286441Srpaulo isperiodic = 1; 5808286441Srpaulo } 5809286441Srpaulo 5810286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) { 5811286441Srpaulo handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX); 5812286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); 5813286441Srpaulo 5814286441Srpaulo iwm_notif_intr(sc); 5815286441Srpaulo 5816286441Srpaulo /* enable periodic interrupt, see above */ 5817286441Srpaulo if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic) 5818286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, 5819286441Srpaulo IWM_CSR_INT_PERIODIC_ENA); 5820286441Srpaulo } 5821286441Srpaulo 5822286441Srpaulo if (__predict_false(r1 & ~handled)) 5823286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5824286441Srpaulo "%s: unhandled interrupts: %x\n", __func__, r1); 5825286441Srpaulo rv = 1; 5826286441Srpaulo 5827286441Srpaulo out_ena: 5828286441Srpaulo iwm_restore_interrupts(sc); 5829286441Srpaulo out: 5830286441Srpaulo IWM_UNLOCK(sc); 5831286441Srpaulo return; 5832286441Srpaulo} 5833286441Srpaulo 5834286441Srpaulo/* 5835286441Srpaulo * Autoconf glue-sniffing 5836286441Srpaulo */ 5837286441Srpaulo#define PCI_VENDOR_INTEL 0x8086 5838286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_1 0x08b3 5839286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_2 0x08b4 5840303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_1 0x3165 5841303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_2 0x3166 5842286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_1 0x08b1 5843286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_2 0x08b2 5844286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_1 0x095a 5845286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_2 0x095b 5846303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_1 0x24f3 5847303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_2 0x24f4 5848330227Seadler#define PCI_PRODUCT_INTEL_WL_8265_1 0x24fd 5849286441Srpaulo 5850286441Srpaulostatic const struct iwm_devices { 5851330187Seadler uint16_t device; 5852330187Seadler const struct iwm_cfg *cfg; 5853286441Srpaulo} iwm_devices[] = { 5854330188Seadler { PCI_PRODUCT_INTEL_WL_3160_1, &iwm3160_cfg }, 5855330188Seadler { PCI_PRODUCT_INTEL_WL_3160_2, &iwm3160_cfg }, 5856330188Seadler { PCI_PRODUCT_INTEL_WL_3165_1, &iwm3165_cfg }, 5857330188Seadler { PCI_PRODUCT_INTEL_WL_3165_2, &iwm3165_cfg }, 5858330188Seadler { PCI_PRODUCT_INTEL_WL_7260_1, &iwm7260_cfg }, 5859330188Seadler { PCI_PRODUCT_INTEL_WL_7260_2, &iwm7260_cfg }, 5860330188Seadler { PCI_PRODUCT_INTEL_WL_7265_1, &iwm7265_cfg }, 5861330188Seadler { PCI_PRODUCT_INTEL_WL_7265_2, &iwm7265_cfg }, 5862330188Seadler { PCI_PRODUCT_INTEL_WL_8260_1, &iwm8260_cfg }, 5863330188Seadler { PCI_PRODUCT_INTEL_WL_8260_2, &iwm8260_cfg }, 5864330227Seadler { PCI_PRODUCT_INTEL_WL_8265_1, &iwm8265_cfg }, 5865286441Srpaulo}; 5866286441Srpaulo 5867286441Srpaulostatic int 5868286441Srpauloiwm_probe(device_t dev) 5869286441Srpaulo{ 5870286441Srpaulo int i; 5871286441Srpaulo 5872303628Ssbruno for (i = 0; i < nitems(iwm_devices); i++) { 5873286441Srpaulo if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && 5874286441Srpaulo pci_get_device(dev) == iwm_devices[i].device) { 5875330188Seadler device_set_desc(dev, iwm_devices[i].cfg->name); 5876286441Srpaulo return (BUS_PROBE_DEFAULT); 5877286441Srpaulo } 5878303628Ssbruno } 5879286441Srpaulo 5880286441Srpaulo return (ENXIO); 5881286441Srpaulo} 5882286441Srpaulo 5883286441Srpaulostatic int 5884286441Srpauloiwm_dev_check(device_t dev) 5885286441Srpaulo{ 5886286441Srpaulo struct iwm_softc *sc; 5887330187Seadler uint16_t devid; 5888330187Seadler int i; 5889286441Srpaulo 5890286441Srpaulo sc = device_get_softc(dev); 5891286441Srpaulo 5892330187Seadler devid = pci_get_device(dev); 5893330187Seadler for (i = 0; i < nitems(iwm_devices); i++) { 5894330187Seadler if (iwm_devices[i].device == devid) { 5895330187Seadler sc->cfg = iwm_devices[i].cfg; 5896330187Seadler return (0); 5897330187Seadler } 5898286441Srpaulo } 5899330187Seadler device_printf(dev, "unknown adapter type\n"); 5900330187Seadler return ENXIO; 5901286441Srpaulo} 5902286441Srpaulo 5903330140Seadler/* PCI registers */ 5904330140Seadler#define PCI_CFG_RETRY_TIMEOUT 0x041 5905330140Seadler 5906286441Srpaulostatic int 5907286441Srpauloiwm_pci_attach(device_t dev) 5908286441Srpaulo{ 5909286441Srpaulo struct iwm_softc *sc; 5910286441Srpaulo int count, error, rid; 5911286441Srpaulo uint16_t reg; 5912286441Srpaulo 5913286441Srpaulo sc = device_get_softc(dev); 5914286441Srpaulo 5915330140Seadler /* We disable the RETRY_TIMEOUT register (0x41) to keep 5916330140Seadler * PCI Tx retries from interfering with C3 CPU state */ 5917330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 5918286441Srpaulo 5919286441Srpaulo /* Enable bus-mastering and hardware bug workaround. */ 5920286441Srpaulo pci_enable_busmaster(dev); 5921286441Srpaulo reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg)); 5922286441Srpaulo /* if !MSI */ 5923286441Srpaulo if (reg & PCIM_STATUS_INTxSTATE) { 5924286441Srpaulo reg &= ~PCIM_STATUS_INTxSTATE; 5925286441Srpaulo } 5926286441Srpaulo pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg)); 5927286441Srpaulo 5928286441Srpaulo rid = PCIR_BAR(0); 5929286441Srpaulo sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 5930286441Srpaulo RF_ACTIVE); 5931286441Srpaulo if (sc->sc_mem == NULL) { 5932286441Srpaulo device_printf(sc->sc_dev, "can't map mem space\n"); 5933286441Srpaulo return (ENXIO); 5934286441Srpaulo } 5935286441Srpaulo sc->sc_st = rman_get_bustag(sc->sc_mem); 5936286441Srpaulo sc->sc_sh = rman_get_bushandle(sc->sc_mem); 5937286441Srpaulo 5938286441Srpaulo /* Install interrupt handler. */ 5939286441Srpaulo count = 1; 5940286441Srpaulo rid = 0; 5941286441Srpaulo if (pci_alloc_msi(dev, &count) == 0) 5942286441Srpaulo rid = 1; 5943286441Srpaulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 5944286441Srpaulo (rid != 0 ? 0 : RF_SHAREABLE)); 5945286441Srpaulo if (sc->sc_irq == NULL) { 5946286441Srpaulo device_printf(dev, "can't map interrupt\n"); 5947286441Srpaulo return (ENXIO); 5948286441Srpaulo } 5949286441Srpaulo error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 5950286441Srpaulo NULL, iwm_intr, sc, &sc->sc_ih); 5951286441Srpaulo if (sc->sc_ih == NULL) { 5952286441Srpaulo device_printf(dev, "can't establish interrupt"); 5953286441Srpaulo return (ENXIO); 5954286441Srpaulo } 5955286441Srpaulo sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); 5956286441Srpaulo 5957286441Srpaulo return (0); 5958286441Srpaulo} 5959286441Srpaulo 5960286441Srpaulostatic void 5961286441Srpauloiwm_pci_detach(device_t dev) 5962286441Srpaulo{ 5963286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 5964286441Srpaulo 5965286441Srpaulo if (sc->sc_irq != NULL) { 5966286441Srpaulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 5967286441Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 5968286441Srpaulo rman_get_rid(sc->sc_irq), sc->sc_irq); 5969286441Srpaulo pci_release_msi(dev); 5970286441Srpaulo } 5971286441Srpaulo if (sc->sc_mem != NULL) 5972286441Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, 5973286441Srpaulo rman_get_rid(sc->sc_mem), sc->sc_mem); 5974286441Srpaulo} 5975286441Srpaulo 5976286441Srpaulo 5977286441Srpaulo 5978286441Srpaulostatic int 5979286441Srpauloiwm_attach(device_t dev) 5980286441Srpaulo{ 5981287197Sglebius struct iwm_softc *sc = device_get_softc(dev); 5982287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5983286441Srpaulo int error; 5984286441Srpaulo int txq_i, i; 5985286441Srpaulo 5986286441Srpaulo sc->sc_dev = dev; 5987330164Seadler sc->sc_attached = 1; 5988293099Savos IWM_LOCK_INIT(sc); 5989287197Sglebius mbufq_init(&sc->sc_snd, ifqmaxlen); 5990286441Srpaulo callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); 5991301187Sadrian callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0); 5992286441Srpaulo TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc); 5993286441Srpaulo 5994330170Seadler sc->sc_notif_wait = iwm_notification_wait_init(sc); 5995330170Seadler if (sc->sc_notif_wait == NULL) { 5996330170Seadler device_printf(dev, "failed to init notification wait struct\n"); 5997330170Seadler goto fail; 5998330170Seadler } 5999330170Seadler 6000330163Seadler /* Init phy db */ 6001330163Seadler sc->sc_phy_db = iwm_phy_db_init(sc); 6002330163Seadler if (!sc->sc_phy_db) { 6003330163Seadler device_printf(dev, "Cannot init phy_db\n"); 6004330163Seadler goto fail; 6005330163Seadler } 6006330163Seadler 6007286441Srpaulo /* PCI attach */ 6008286441Srpaulo error = iwm_pci_attach(dev); 6009286441Srpaulo if (error != 0) 6010286441Srpaulo goto fail; 6011286441Srpaulo 6012286441Srpaulo sc->sc_wantresp = -1; 6013286441Srpaulo 6014286441Srpaulo /* Check device type */ 6015286441Srpaulo error = iwm_dev_check(dev); 6016286441Srpaulo if (error != 0) 6017286441Srpaulo goto fail; 6018286441Srpaulo 6019330166Seadler sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); 6020286441Srpaulo /* 6021303628Ssbruno * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have 6022303628Ssbruno * changed, and now the revision step also includes bit 0-1 (no more 6023303628Ssbruno * "dash" value). To keep hw_rev backwards compatible - we'll store it 6024303628Ssbruno * in the old format. 6025303628Ssbruno */ 6026330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 6027303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | 6028303628Ssbruno (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); 6029303628Ssbruno 6030286441Srpaulo if (iwm_prepare_card_hw(sc) != 0) { 6031286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 6032286441Srpaulo goto fail; 6033286441Srpaulo } 6034286441Srpaulo 6035330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 6036303628Ssbruno int ret; 6037303628Ssbruno uint32_t hw_step; 6038303628Ssbruno 6039303628Ssbruno /* 6040303628Ssbruno * In order to recognize C step the driver should read the 6041303628Ssbruno * chip version id located at the AUX bus MISC address. 6042303628Ssbruno */ 6043303628Ssbruno IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, 6044303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 6045303628Ssbruno DELAY(2); 6046303628Ssbruno 6047303628Ssbruno ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, 6048303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 6049303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 6050303628Ssbruno 25000); 6051330149Seadler if (!ret) { 6052303628Ssbruno device_printf(sc->sc_dev, 6053303628Ssbruno "Failed to wake up the nic\n"); 6054303628Ssbruno goto fail; 6055303628Ssbruno } 6056303628Ssbruno 6057303628Ssbruno if (iwm_nic_lock(sc)) { 6058303628Ssbruno hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); 6059303628Ssbruno hw_step |= IWM_ENABLE_WFPM; 6060303628Ssbruno iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); 6061303628Ssbruno hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); 6062303628Ssbruno hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; 6063303628Ssbruno if (hw_step == 0x3) 6064303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | 6065303628Ssbruno (IWM_SILICON_C_STEP << 2); 6066303628Ssbruno iwm_nic_unlock(sc); 6067303628Ssbruno } else { 6068303628Ssbruno device_printf(sc->sc_dev, "Failed to lock the nic\n"); 6069303628Ssbruno goto fail; 6070303628Ssbruno } 6071303628Ssbruno } 6072303628Ssbruno 6073330166Seadler /* special-case 7265D, it has the same PCI IDs. */ 6074330166Seadler if (sc->cfg == &iwm7265_cfg && 6075330166Seadler (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == IWM_CSR_HW_REV_TYPE_7265D) { 6076330166Seadler sc->cfg = &iwm7265d_cfg; 6077330166Seadler } 6078330166Seadler 6079286441Srpaulo /* Allocate DMA memory for firmware transfers. */ 6080286441Srpaulo if ((error = iwm_alloc_fwmem(sc)) != 0) { 6081286441Srpaulo device_printf(dev, "could not allocate memory for firmware\n"); 6082286441Srpaulo goto fail; 6083286441Srpaulo } 6084286441Srpaulo 6085286441Srpaulo /* Allocate "Keep Warm" page. */ 6086286441Srpaulo if ((error = iwm_alloc_kw(sc)) != 0) { 6087286441Srpaulo device_printf(dev, "could not allocate keep warm page\n"); 6088286441Srpaulo goto fail; 6089286441Srpaulo } 6090286441Srpaulo 6091286441Srpaulo /* We use ICT interrupts */ 6092286441Srpaulo if ((error = iwm_alloc_ict(sc)) != 0) { 6093286441Srpaulo device_printf(dev, "could not allocate ICT table\n"); 6094286441Srpaulo goto fail; 6095286441Srpaulo } 6096286441Srpaulo 6097286441Srpaulo /* Allocate TX scheduler "rings". */ 6098286441Srpaulo if ((error = iwm_alloc_sched(sc)) != 0) { 6099286441Srpaulo device_printf(dev, "could not allocate TX scheduler rings\n"); 6100286441Srpaulo goto fail; 6101286441Srpaulo } 6102286441Srpaulo 6103286441Srpaulo /* Allocate TX rings */ 6104286441Srpaulo for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) { 6105286441Srpaulo if ((error = iwm_alloc_tx_ring(sc, 6106286441Srpaulo &sc->txq[txq_i], txq_i)) != 0) { 6107286441Srpaulo device_printf(dev, 6108286441Srpaulo "could not allocate TX ring %d\n", 6109286441Srpaulo txq_i); 6110286441Srpaulo goto fail; 6111286441Srpaulo } 6112286441Srpaulo } 6113286441Srpaulo 6114286441Srpaulo /* Allocate RX ring. */ 6115286441Srpaulo if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) { 6116286441Srpaulo device_printf(dev, "could not allocate RX ring\n"); 6117286441Srpaulo goto fail; 6118286441Srpaulo } 6119286441Srpaulo 6120286441Srpaulo /* Clear pending interrupts. */ 6121286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); 6122286441Srpaulo 6123286441Srpaulo ic->ic_softc = sc; 6124286441Srpaulo ic->ic_name = device_get_nameunit(sc->sc_dev); 6125286441Srpaulo ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 6126286441Srpaulo ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 6127286441Srpaulo 6128286441Srpaulo /* Set device capabilities. */ 6129286441Srpaulo ic->ic_caps = 6130286441Srpaulo IEEE80211_C_STA | 6131286441Srpaulo IEEE80211_C_WPA | /* WPA/RSN */ 6132286441Srpaulo IEEE80211_C_WME | 6133330201Seadler IEEE80211_C_PMGT | 6134286441Srpaulo IEEE80211_C_SHSLOT | /* short slot time supported */ 6135286441Srpaulo IEEE80211_C_SHPREAMBLE /* short preamble supported */ 6136286441Srpaulo// IEEE80211_C_BGSCAN /* capable of bg scanning */ 6137286441Srpaulo ; 6138330453Seadler /* Advertise full-offload scanning */ 6139330453Seadler ic->ic_flags_ext = IEEE80211_FEXT_SCAN_OFFLOAD; 6140286441Srpaulo for (i = 0; i < nitems(sc->sc_phyctxt); i++) { 6141286441Srpaulo sc->sc_phyctxt[i].id = i; 6142286441Srpaulo sc->sc_phyctxt[i].color = 0; 6143286441Srpaulo sc->sc_phyctxt[i].ref = 0; 6144286441Srpaulo sc->sc_phyctxt[i].channel = NULL; 6145286441Srpaulo } 6146286441Srpaulo 6147330144Seadler /* Default noise floor */ 6148330144Seadler sc->sc_noise = -96; 6149330144Seadler 6150286441Srpaulo /* Max RSSI */ 6151286441Srpaulo sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; 6152330144Seadler 6153286441Srpaulo sc->sc_preinit_hook.ich_func = iwm_preinit; 6154286441Srpaulo sc->sc_preinit_hook.ich_arg = sc; 6155286441Srpaulo if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { 6156286441Srpaulo device_printf(dev, "config_intrhook_establish failed\n"); 6157286441Srpaulo goto fail; 6158286441Srpaulo } 6159286441Srpaulo 6160286441Srpaulo#ifdef IWM_DEBUG 6161286441Srpaulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 6162286441Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", 6163286441Srpaulo CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); 6164286441Srpaulo#endif 6165286441Srpaulo 6166286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6167286441Srpaulo "<-%s\n", __func__); 6168286441Srpaulo 6169286441Srpaulo return 0; 6170286441Srpaulo 6171286441Srpaulo /* Free allocated memory if something failed during attachment. */ 6172286441Srpaulofail: 6173286441Srpaulo iwm_detach_local(sc, 0); 6174286441Srpaulo 6175286441Srpaulo return ENXIO; 6176286441Srpaulo} 6177286441Srpaulo 6178286441Srpaulostatic int 6179303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr) 6180303628Ssbruno{ 6181303628Ssbruno char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; 6182303628Ssbruno 6183303628Ssbruno if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr)) 6184303628Ssbruno return (FALSE); 6185303628Ssbruno 6186303628Ssbruno return (TRUE); 6187303628Ssbruno} 6188303628Ssbruno 6189303628Ssbrunostatic int 6190330209Seadleriwm_wme_update(struct ieee80211com *ic) 6191286441Srpaulo{ 6192330209Seadler#define IWM_EXP2(x) ((1 << (x)) - 1) /* CWmin = 2^ECWmin - 1 */ 6193286865Sadrian struct iwm_softc *sc = ic->ic_softc; 6194330209Seadler struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6195330209Seadler struct iwm_vap *ivp = IWM_VAP(vap); 6196330209Seadler struct iwm_node *in; 6197330209Seadler struct wmeParams tmp[WME_NUM_AC]; 6198330209Seadler int aci, error; 6199286441Srpaulo 6200330209Seadler if (vap == NULL) 6201330209Seadler return (0); 6202330209Seadler 6203330209Seadler IEEE80211_LOCK(ic); 6204330209Seadler for (aci = 0; aci < WME_NUM_AC; aci++) 6205330209Seadler tmp[aci] = ic->ic_wme.wme_chanParams.cap_wmeParams[aci]; 6206330209Seadler IEEE80211_UNLOCK(ic); 6207330209Seadler 6208330209Seadler IWM_LOCK(sc); 6209330209Seadler for (aci = 0; aci < WME_NUM_AC; aci++) { 6210330209Seadler const struct wmeParams *ac = &tmp[aci]; 6211330209Seadler ivp->queue_params[aci].aifsn = ac->wmep_aifsn; 6212330209Seadler ivp->queue_params[aci].cw_min = IWM_EXP2(ac->wmep_logcwmin); 6213330209Seadler ivp->queue_params[aci].cw_max = IWM_EXP2(ac->wmep_logcwmax); 6214330209Seadler ivp->queue_params[aci].edca_txop = 6215330209Seadler IEEE80211_TXOP_TO_US(ac->wmep_txopLimit); 6216330209Seadler } 6217330209Seadler ivp->have_wme = TRUE; 6218330209Seadler if (ivp->is_uploaded && vap->iv_bss != NULL) { 6219330209Seadler in = IWM_NODE(vap->iv_bss); 6220330209Seadler if (in->in_assoc) { 6221330209Seadler if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 6222330209Seadler device_printf(sc->sc_dev, 6223330209Seadler "%s: failed to update MAC\n", __func__); 6224330209Seadler } 6225330209Seadler } 6226330209Seadler } 6227330209Seadler IWM_UNLOCK(sc); 6228330209Seadler 6229286441Srpaulo return (0); 6230330209Seadler#undef IWM_EXP2 6231286441Srpaulo} 6232286441Srpaulo 6233286441Srpaulostatic void 6234286441Srpauloiwm_preinit(void *arg) 6235286441Srpaulo{ 6236286441Srpaulo struct iwm_softc *sc = arg; 6237286441Srpaulo device_t dev = sc->sc_dev; 6238287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 6239286441Srpaulo int error; 6240286441Srpaulo 6241286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6242286441Srpaulo "->%s\n", __func__); 6243286441Srpaulo 6244286441Srpaulo IWM_LOCK(sc); 6245286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 6246286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 6247286441Srpaulo IWM_UNLOCK(sc); 6248286441Srpaulo goto fail; 6249286441Srpaulo } 6250286441Srpaulo 6251286441Srpaulo error = iwm_run_init_mvm_ucode(sc, 1); 6252286441Srpaulo iwm_stop_device(sc); 6253286441Srpaulo if (error) { 6254286441Srpaulo IWM_UNLOCK(sc); 6255286441Srpaulo goto fail; 6256286441Srpaulo } 6257286441Srpaulo device_printf(dev, 6258303628Ssbruno "hw rev 0x%x, fw ver %s, address %s\n", 6259286441Srpaulo sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, 6260330165Seadler sc->sc_fwver, ether_sprintf(sc->nvm_data->hw_addr)); 6261286441Srpaulo 6262286441Srpaulo /* not all hardware can do 5GHz band */ 6263330165Seadler if (!sc->nvm_data->sku_cap_band_52GHz_enable) 6264286441Srpaulo memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, 6265286441Srpaulo sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A])); 6266286441Srpaulo IWM_UNLOCK(sc); 6267286441Srpaulo 6268298877Savos iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, 6269298877Savos ic->ic_channels); 6270298877Savos 6271286441Srpaulo /* 6272286441Srpaulo * At this point we've committed - if we fail to do setup, 6273286441Srpaulo * we now also have to tear down the net80211 state. 6274286441Srpaulo */ 6275287197Sglebius ieee80211_ifattach(ic); 6276286441Srpaulo ic->ic_vap_create = iwm_vap_create; 6277286441Srpaulo ic->ic_vap_delete = iwm_vap_delete; 6278286441Srpaulo ic->ic_raw_xmit = iwm_raw_xmit; 6279286441Srpaulo ic->ic_node_alloc = iwm_node_alloc; 6280286441Srpaulo ic->ic_scan_start = iwm_scan_start; 6281286441Srpaulo ic->ic_scan_end = iwm_scan_end; 6282286441Srpaulo ic->ic_update_mcast = iwm_update_mcast; 6283298877Savos ic->ic_getradiocaps = iwm_init_channel_map; 6284286441Srpaulo ic->ic_set_channel = iwm_set_channel; 6285286441Srpaulo ic->ic_scan_curchan = iwm_scan_curchan; 6286286441Srpaulo ic->ic_scan_mindwell = iwm_scan_mindwell; 6287330209Seadler ic->ic_wme.wme_update = iwm_wme_update; 6288287197Sglebius ic->ic_parent = iwm_parent; 6289287197Sglebius ic->ic_transmit = iwm_transmit; 6290286441Srpaulo iwm_radiotap_attach(sc); 6291286441Srpaulo if (bootverbose) 6292286441Srpaulo ieee80211_announce(ic); 6293286441Srpaulo 6294286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6295286441Srpaulo "<-%s\n", __func__); 6296286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 6297286441Srpaulo 6298286441Srpaulo return; 6299286441Srpaulofail: 6300286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 6301286441Srpaulo iwm_detach_local(sc, 0); 6302286441Srpaulo} 6303286441Srpaulo 6304286441Srpaulo/* 6305286441Srpaulo * Attach the interface to 802.11 radiotap. 6306286441Srpaulo */ 6307286441Srpaulostatic void 6308286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc) 6309286441Srpaulo{ 6310287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 6311286441Srpaulo 6312286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6313286441Srpaulo "->%s begin\n", __func__); 6314286441Srpaulo ieee80211_radiotap_attach(ic, 6315286441Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 6316286441Srpaulo IWM_TX_RADIOTAP_PRESENT, 6317286441Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 6318286441Srpaulo IWM_RX_RADIOTAP_PRESENT); 6319286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6320286441Srpaulo "->%s end\n", __func__); 6321286441Srpaulo} 6322286441Srpaulo 6323286441Srpaulostatic struct ieee80211vap * 6324286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 6325286441Srpaulo enum ieee80211_opmode opmode, int flags, 6326286441Srpaulo const uint8_t bssid[IEEE80211_ADDR_LEN], 6327286441Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]) 6328286441Srpaulo{ 6329286441Srpaulo struct iwm_vap *ivp; 6330286441Srpaulo struct ieee80211vap *vap; 6331286441Srpaulo 6332286441Srpaulo if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 6333286441Srpaulo return NULL; 6334287197Sglebius ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO); 6335286441Srpaulo vap = &ivp->iv_vap; 6336287197Sglebius ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); 6337286441Srpaulo vap->iv_bmissthreshold = 10; /* override default */ 6338286441Srpaulo /* Override with driver methods. */ 6339286441Srpaulo ivp->iv_newstate = vap->iv_newstate; 6340286441Srpaulo vap->iv_newstate = iwm_newstate; 6341286441Srpaulo 6342330204Seadler ivp->id = IWM_DEFAULT_MACID; 6343330204Seadler ivp->color = IWM_DEFAULT_COLOR; 6344330204Seadler 6345330209Seadler ivp->have_wme = FALSE; 6346330209Seadler 6347286441Srpaulo ieee80211_ratectl_init(vap); 6348286441Srpaulo /* Complete setup. */ 6349287197Sglebius ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status, 6350287197Sglebius mac); 6351286441Srpaulo ic->ic_opmode = opmode; 6352286441Srpaulo 6353286441Srpaulo return vap; 6354286441Srpaulo} 6355286441Srpaulo 6356286441Srpaulostatic void 6357286441Srpauloiwm_vap_delete(struct ieee80211vap *vap) 6358286441Srpaulo{ 6359286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 6360286441Srpaulo 6361286441Srpaulo ieee80211_ratectl_deinit(vap); 6362286441Srpaulo ieee80211_vap_detach(vap); 6363286441Srpaulo free(ivp, M_80211_VAP); 6364286441Srpaulo} 6365286441Srpaulo 6366286441Srpaulostatic void 6367330223Seadleriwm_xmit_queue_drain(struct iwm_softc *sc) 6368330223Seadler{ 6369330223Seadler struct mbuf *m; 6370330223Seadler struct ieee80211_node *ni; 6371330223Seadler 6372330223Seadler while ((m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 6373330223Seadler ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 6374330223Seadler ieee80211_free_node(ni); 6375330223Seadler m_freem(m); 6376330223Seadler } 6377330223Seadler} 6378330223Seadler 6379330223Seadlerstatic void 6380286441Srpauloiwm_scan_start(struct ieee80211com *ic) 6381286441Srpaulo{ 6382286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6383303628Ssbruno struct iwm_softc *sc = ic->ic_softc; 6384286441Srpaulo int error; 6385286441Srpaulo 6386286441Srpaulo IWM_LOCK(sc); 6387330174Seadler if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) { 6388330174Seadler /* This should not be possible */ 6389330174Seadler device_printf(sc->sc_dev, 6390330174Seadler "%s: Previous scan not completed yet\n", __func__); 6391330174Seadler } 6392330196Seadler if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) 6393303628Ssbruno error = iwm_mvm_umac_scan(sc); 6394303628Ssbruno else 6395303628Ssbruno error = iwm_mvm_lmac_scan(sc); 6396303628Ssbruno if (error != 0) { 6397330174Seadler device_printf(sc->sc_dev, "could not initiate scan\n"); 6398286441Srpaulo IWM_UNLOCK(sc); 6399286441Srpaulo ieee80211_cancel_scan(vap); 6400301187Sadrian } else { 6401330174Seadler sc->sc_flags |= IWM_FLAG_SCAN_RUNNING; 6402301187Sadrian iwm_led_blink_start(sc); 6403286441Srpaulo IWM_UNLOCK(sc); 6404301187Sadrian } 6405286441Srpaulo} 6406286441Srpaulo 6407286441Srpaulostatic void 6408286441Srpauloiwm_scan_end(struct ieee80211com *ic) 6409286441Srpaulo{ 6410301187Sadrian struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6411301187Sadrian struct iwm_softc *sc = ic->ic_softc; 6412301187Sadrian 6413301187Sadrian IWM_LOCK(sc); 6414301187Sadrian iwm_led_blink_stop(sc); 6415301187Sadrian if (vap->iv_state == IEEE80211_S_RUN) 6416301187Sadrian iwm_mvm_led_enable(sc); 6417330174Seadler if (sc->sc_flags & IWM_FLAG_SCAN_RUNNING) { 6418330174Seadler /* 6419330174Seadler * Removing IWM_FLAG_SCAN_RUNNING now, is fine because 6420330174Seadler * both iwm_scan_end and iwm_scan_start run in the ic->ic_tq 6421330174Seadler * taskqueue. 6422330174Seadler */ 6423330174Seadler sc->sc_flags &= ~IWM_FLAG_SCAN_RUNNING; 6424330174Seadler iwm_mvm_scan_stop_wait(sc); 6425330174Seadler } 6426301187Sadrian IWM_UNLOCK(sc); 6427330174Seadler 6428330174Seadler /* 6429330174Seadler * Make sure we don't race, if sc_es_task is still enqueued here. 6430330174Seadler * This is to make sure that it won't call ieee80211_scan_done 6431330174Seadler * when we have already started the next scan. 6432330174Seadler */ 6433330174Seadler taskqueue_cancel(ic->ic_tq, &sc->sc_es_task, NULL); 6434286441Srpaulo} 6435286441Srpaulo 6436286441Srpaulostatic void 6437286441Srpauloiwm_update_mcast(struct ieee80211com *ic) 6438286441Srpaulo{ 6439286441Srpaulo} 6440286441Srpaulo 6441286441Srpaulostatic void 6442286441Srpauloiwm_set_channel(struct ieee80211com *ic) 6443286441Srpaulo{ 6444286441Srpaulo} 6445286441Srpaulo 6446286441Srpaulostatic void 6447286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 6448286441Srpaulo{ 6449286441Srpaulo} 6450286441Srpaulo 6451286441Srpaulostatic void 6452286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss) 6453286441Srpaulo{ 6454286441Srpaulo return; 6455286441Srpaulo} 6456286441Srpaulo 6457286441Srpaulovoid 6458286441Srpauloiwm_init_task(void *arg1) 6459286441Srpaulo{ 6460286441Srpaulo struct iwm_softc *sc = arg1; 6461286441Srpaulo 6462286441Srpaulo IWM_LOCK(sc); 6463286441Srpaulo while (sc->sc_flags & IWM_FLAG_BUSY) 6464286441Srpaulo msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0); 6465286441Srpaulo sc->sc_flags |= IWM_FLAG_BUSY; 6466287197Sglebius iwm_stop(sc); 6467287197Sglebius if (sc->sc_ic.ic_nrunning > 0) 6468286441Srpaulo iwm_init(sc); 6469286441Srpaulo sc->sc_flags &= ~IWM_FLAG_BUSY; 6470286441Srpaulo wakeup(&sc->sc_flags); 6471286441Srpaulo IWM_UNLOCK(sc); 6472286441Srpaulo} 6473286441Srpaulo 6474286441Srpaulostatic int 6475286441Srpauloiwm_resume(device_t dev) 6476286441Srpaulo{ 6477298612Sadrian struct iwm_softc *sc = device_get_softc(dev); 6478298612Sadrian int do_reinit = 0; 6479286441Srpaulo 6480330140Seadler /* 6481330140Seadler * We disable the RETRY_TIMEOUT register (0x41) to keep 6482330140Seadler * PCI Tx retries from interfering with C3 CPU state. 6483330140Seadler */ 6484330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 6485286441Srpaulo iwm_init_task(device_get_softc(dev)); 6486286441Srpaulo 6487298612Sadrian IWM_LOCK(sc); 6488303628Ssbruno if (sc->sc_flags & IWM_FLAG_SCANNING) { 6489303628Ssbruno sc->sc_flags &= ~IWM_FLAG_SCANNING; 6490298612Sadrian do_reinit = 1; 6491298612Sadrian } 6492298612Sadrian IWM_UNLOCK(sc); 6493298612Sadrian 6494298612Sadrian if (do_reinit) 6495298612Sadrian ieee80211_resume_all(&sc->sc_ic); 6496298612Sadrian 6497286441Srpaulo return 0; 6498286441Srpaulo} 6499286441Srpaulo 6500286441Srpaulostatic int 6501286441Srpauloiwm_suspend(device_t dev) 6502286441Srpaulo{ 6503298612Sadrian int do_stop = 0; 6504286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6505286441Srpaulo 6506298612Sadrian do_stop = !! (sc->sc_ic.ic_nrunning > 0); 6507298612Sadrian 6508298612Sadrian ieee80211_suspend_all(&sc->sc_ic); 6509298612Sadrian 6510298612Sadrian if (do_stop) { 6511287197Sglebius IWM_LOCK(sc); 6512287197Sglebius iwm_stop(sc); 6513303628Ssbruno sc->sc_flags |= IWM_FLAG_SCANNING; 6514287197Sglebius IWM_UNLOCK(sc); 6515287197Sglebius } 6516286441Srpaulo 6517286441Srpaulo return (0); 6518286441Srpaulo} 6519286441Srpaulo 6520286441Srpaulostatic int 6521286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211) 6522286441Srpaulo{ 6523293219Savos struct iwm_fw_info *fw = &sc->sc_fw; 6524286441Srpaulo device_t dev = sc->sc_dev; 6525286441Srpaulo int i; 6526286441Srpaulo 6527330164Seadler if (!sc->sc_attached) 6528330164Seadler return 0; 6529330164Seadler sc->sc_attached = 0; 6530330164Seadler 6531330159Seadler if (do_net80211) 6532330159Seadler ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task); 6533303628Ssbruno 6534301187Sadrian callout_drain(&sc->sc_led_blink_to); 6535287197Sglebius callout_drain(&sc->sc_watchdog_to); 6536287197Sglebius iwm_stop_device(sc); 6537303628Ssbruno if (do_net80211) { 6538330223Seadler IWM_LOCK(sc); 6539330223Seadler iwm_xmit_queue_drain(sc); 6540330223Seadler IWM_UNLOCK(sc); 6541287197Sglebius ieee80211_ifdetach(&sc->sc_ic); 6542303628Ssbruno } 6543286441Srpaulo 6544330163Seadler iwm_phy_db_free(sc->sc_phy_db); 6545330163Seadler sc->sc_phy_db = NULL; 6546302103Sadrian 6547330165Seadler iwm_free_nvm_data(sc->nvm_data); 6548330165Seadler 6549286441Srpaulo /* Free descriptor rings */ 6550301970Sadrian iwm_free_rx_ring(sc, &sc->rxq); 6551286441Srpaulo for (i = 0; i < nitems(sc->txq); i++) 6552286441Srpaulo iwm_free_tx_ring(sc, &sc->txq[i]); 6553286441Srpaulo 6554293219Savos /* Free firmware */ 6555293219Savos if (fw->fw_fp != NULL) 6556293219Savos iwm_fw_info_free(fw); 6557293219Savos 6558293178Savos /* Free scheduler */ 6559330150Seadler iwm_dma_contig_free(&sc->sched_dma); 6560330150Seadler iwm_dma_contig_free(&sc->ict_dma); 6561330150Seadler iwm_dma_contig_free(&sc->kw_dma); 6562330150Seadler iwm_dma_contig_free(&sc->fw_dma); 6563286441Srpaulo 6564330192Seadler iwm_free_fw_paging(sc); 6565330192Seadler 6566286441Srpaulo /* Finished with the hardware - detach things */ 6567286441Srpaulo iwm_pci_detach(dev); 6568286441Srpaulo 6569330170Seadler if (sc->sc_notif_wait != NULL) { 6570330170Seadler iwm_notification_wait_free(sc->sc_notif_wait); 6571330170Seadler sc->sc_notif_wait = NULL; 6572330170Seadler } 6573330170Seadler 6574293099Savos IWM_LOCK_DESTROY(sc); 6575286441Srpaulo 6576286441Srpaulo return (0); 6577286441Srpaulo} 6578286441Srpaulo 6579286441Srpaulostatic int 6580286441Srpauloiwm_detach(device_t dev) 6581286441Srpaulo{ 6582286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6583286441Srpaulo 6584286441Srpaulo return (iwm_detach_local(sc, 1)); 6585286441Srpaulo} 6586286441Srpaulo 6587286441Srpaulostatic device_method_t iwm_pci_methods[] = { 6588286441Srpaulo /* Device interface */ 6589286441Srpaulo DEVMETHOD(device_probe, iwm_probe), 6590286441Srpaulo DEVMETHOD(device_attach, iwm_attach), 6591286441Srpaulo DEVMETHOD(device_detach, iwm_detach), 6592286441Srpaulo DEVMETHOD(device_suspend, iwm_suspend), 6593286441Srpaulo DEVMETHOD(device_resume, iwm_resume), 6594286441Srpaulo 6595286441Srpaulo DEVMETHOD_END 6596286441Srpaulo}; 6597286441Srpaulo 6598286441Srpaulostatic driver_t iwm_pci_driver = { 6599286441Srpaulo "iwm", 6600286441Srpaulo iwm_pci_methods, 6601286441Srpaulo sizeof (struct iwm_softc) 6602286441Srpaulo}; 6603286441Srpaulo 6604286441Srpaulostatic devclass_t iwm_devclass; 6605286441Srpaulo 6606286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL); 6607286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1); 6608286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1); 6609286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1); 6610