if_iwm.c revision 330155
1303628Ssbruno/* $OpenBSD: if_iwm.c,v 1.42 2015/05/30 02:49:23 deraadt 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 330155 2018-03-01 04:58:00Z eadler $"); 107286441Srpaulo 108300248Savos#include "opt_wlan.h" 109300248Savos 110286441Srpaulo#include <sys/param.h> 111286441Srpaulo#include <sys/bus.h> 112286441Srpaulo#include <sys/conf.h> 113286441Srpaulo#include <sys/endian.h> 114286441Srpaulo#include <sys/firmware.h> 115286441Srpaulo#include <sys/kernel.h> 116286441Srpaulo#include <sys/malloc.h> 117286441Srpaulo#include <sys/mbuf.h> 118286441Srpaulo#include <sys/mutex.h> 119286441Srpaulo#include <sys/module.h> 120286441Srpaulo#include <sys/proc.h> 121286441Srpaulo#include <sys/rman.h> 122286441Srpaulo#include <sys/socket.h> 123286441Srpaulo#include <sys/sockio.h> 124286441Srpaulo#include <sys/sysctl.h> 125286441Srpaulo#include <sys/linker.h> 126286441Srpaulo 127286441Srpaulo#include <machine/bus.h> 128286441Srpaulo#include <machine/endian.h> 129286441Srpaulo#include <machine/resource.h> 130286441Srpaulo 131286441Srpaulo#include <dev/pci/pcivar.h> 132286441Srpaulo#include <dev/pci/pcireg.h> 133286441Srpaulo 134286441Srpaulo#include <net/bpf.h> 135286441Srpaulo 136286441Srpaulo#include <net/if.h> 137286441Srpaulo#include <net/if_var.h> 138286441Srpaulo#include <net/if_arp.h> 139286441Srpaulo#include <net/if_dl.h> 140286441Srpaulo#include <net/if_media.h> 141286441Srpaulo#include <net/if_types.h> 142286441Srpaulo 143286441Srpaulo#include <netinet/in.h> 144286441Srpaulo#include <netinet/in_systm.h> 145286441Srpaulo#include <netinet/if_ether.h> 146286441Srpaulo#include <netinet/ip.h> 147286441Srpaulo 148286441Srpaulo#include <net80211/ieee80211_var.h> 149286441Srpaulo#include <net80211/ieee80211_regdomain.h> 150286441Srpaulo#include <net80211/ieee80211_ratectl.h> 151286441Srpaulo#include <net80211/ieee80211_radiotap.h> 152286441Srpaulo 153286475Srpaulo#include <dev/iwm/if_iwmreg.h> 154286475Srpaulo#include <dev/iwm/if_iwmvar.h> 155286475Srpaulo#include <dev/iwm/if_iwm_debug.h> 156286475Srpaulo#include <dev/iwm/if_iwm_util.h> 157286475Srpaulo#include <dev/iwm/if_iwm_binding.h> 158286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h> 159286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h> 160286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h> 161286475Srpaulo#include <dev/iwm/if_iwm_time_event.h> 162286475Srpaulo#include <dev/iwm/if_iwm_power.h> 163286475Srpaulo#include <dev/iwm/if_iwm_scan.h> 164286441Srpaulo 165286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h> 166301187Sadrian#include <dev/iwm/if_iwm_led.h> 167286441Srpaulo 168286441Srpauloconst uint8_t iwm_nvm_channels[] = { 169286441Srpaulo /* 2.4 GHz */ 170286441Srpaulo 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 171286441Srpaulo /* 5 GHz */ 172298877Savos 36, 40, 44, 48, 52, 56, 60, 64, 173286441Srpaulo 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 174286441Srpaulo 149, 153, 157, 161, 165 175286441Srpaulo}; 176298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS, 177298877Savos "IWM_NUM_CHANNELS is too small"); 178298877Savos 179303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = { 180303628Ssbruno /* 2.4 GHz */ 181303628Ssbruno 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 182303628Ssbruno /* 5 GHz */ 183303628Ssbruno 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 184303628Ssbruno 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 185303628Ssbruno 149, 153, 157, 161, 165, 169, 173, 177, 181 186303628Ssbruno}; 187303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000, 188303628Ssbruno "IWM_NUM_CHANNELS_8000 is too small"); 189303628Ssbruno 190303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS 14 191303628Ssbruno#define IWM_N_HW_ADDR_MASK 0xF 192303628Ssbruno 193286441Srpaulo/* 194286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries 195286441Srpaulo * that are populated. 196286441Srpaulo */ 197286441Srpauloconst struct iwm_rate { 198286441Srpaulo uint8_t rate; 199286441Srpaulo uint8_t plcp; 200286441Srpaulo} iwm_rates[] = { 201286441Srpaulo { 2, IWM_RATE_1M_PLCP }, 202286441Srpaulo { 4, IWM_RATE_2M_PLCP }, 203286441Srpaulo { 11, IWM_RATE_5M_PLCP }, 204286441Srpaulo { 22, IWM_RATE_11M_PLCP }, 205286441Srpaulo { 12, IWM_RATE_6M_PLCP }, 206286441Srpaulo { 18, IWM_RATE_9M_PLCP }, 207286441Srpaulo { 24, IWM_RATE_12M_PLCP }, 208286441Srpaulo { 36, IWM_RATE_18M_PLCP }, 209286441Srpaulo { 48, IWM_RATE_24M_PLCP }, 210286441Srpaulo { 72, IWM_RATE_36M_PLCP }, 211286441Srpaulo { 96, IWM_RATE_48M_PLCP }, 212286441Srpaulo { 108, IWM_RATE_54M_PLCP }, 213286441Srpaulo}; 214286441Srpaulo#define IWM_RIDX_CCK 0 215286441Srpaulo#define IWM_RIDX_OFDM 4 216286441Srpaulo#define IWM_RIDX_MAX (nitems(iwm_rates)-1) 217286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) 218286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) 219286441Srpaulo 220303628Ssbrunostruct iwm_nvm_section { 221303628Ssbruno uint16_t length; 222303628Ssbruno uint8_t *data; 223303628Ssbruno}; 224303628Ssbruno 225286441Srpaulostatic int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); 226286441Srpaulostatic int iwm_firmware_store_section(struct iwm_softc *, 227286441Srpaulo enum iwm_ucode_type, 228286441Srpaulo const uint8_t *, size_t); 229286441Srpaulostatic int iwm_set_default_calib(struct iwm_softc *, const void *); 230286441Srpaulostatic void iwm_fw_info_free(struct iwm_fw_info *); 231286441Srpaulostatic int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); 232286441Srpaulostatic void iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int); 233286441Srpaulostatic int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, 234286441Srpaulo bus_size_t, bus_size_t); 235286441Srpaulostatic void iwm_dma_contig_free(struct iwm_dma_info *); 236286441Srpaulostatic int iwm_alloc_fwmem(struct iwm_softc *); 237286441Srpaulostatic int iwm_alloc_sched(struct iwm_softc *); 238286441Srpaulostatic int iwm_alloc_kw(struct iwm_softc *); 239286441Srpaulostatic int iwm_alloc_ict(struct iwm_softc *); 240286441Srpaulostatic int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 241301191Sadrianstatic void iwm_disable_rx_dma(struct iwm_softc *); 242286441Srpaulostatic void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 243286441Srpaulostatic void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 244286441Srpaulostatic int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, 245286441Srpaulo int); 246286441Srpaulostatic void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 247286441Srpaulostatic void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 248286441Srpaulostatic void iwm_enable_interrupts(struct iwm_softc *); 249286441Srpaulostatic void iwm_restore_interrupts(struct iwm_softc *); 250286441Srpaulostatic void iwm_disable_interrupts(struct iwm_softc *); 251286441Srpaulostatic void iwm_ict_reset(struct iwm_softc *); 252286441Srpaulostatic int iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *); 253286441Srpaulostatic void iwm_stop_device(struct iwm_softc *); 254286441Srpaulostatic void iwm_mvm_nic_config(struct iwm_softc *); 255286441Srpaulostatic int iwm_nic_rx_init(struct iwm_softc *); 256286441Srpaulostatic int iwm_nic_tx_init(struct iwm_softc *); 257286441Srpaulostatic int iwm_nic_init(struct iwm_softc *); 258303628Ssbrunostatic int iwm_enable_txq(struct iwm_softc *, int, int, int); 259286441Srpaulostatic int iwm_post_alive(struct iwm_softc *); 260286441Srpaulostatic int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, 261286441Srpaulo uint16_t, uint8_t *, uint16_t *); 262286441Srpaulostatic int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, 263303628Ssbruno uint16_t *, size_t); 264298877Savosstatic uint32_t iwm_eeprom_channel_flags(uint16_t); 265298877Savosstatic void iwm_add_channel_band(struct iwm_softc *, 266303628Ssbruno struct ieee80211_channel[], int, int *, int, size_t, 267298877Savos const uint8_t[]); 268298877Savosstatic void iwm_init_channel_map(struct ieee80211com *, int, int *, 269298877Savos struct ieee80211_channel[]); 270286441Srpaulostatic int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, 271303628Ssbruno const uint16_t *, const uint16_t *, 272303628Ssbruno const uint16_t *, const uint16_t *, 273303628Ssbruno const uint16_t *); 274303628Ssbrunostatic void iwm_set_hw_address_8000(struct iwm_softc *, 275303628Ssbruno struct iwm_nvm_data *, 276303628Ssbruno const uint16_t *, const uint16_t *); 277303628Ssbrunostatic int iwm_get_sku(const struct iwm_softc *, const uint16_t *, 278303628Ssbruno const uint16_t *); 279303628Ssbrunostatic int iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *); 280303628Ssbrunostatic int iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *, 281303628Ssbruno const uint16_t *); 282303628Ssbrunostatic int iwm_get_n_hw_addrs(const struct iwm_softc *, 283303628Ssbruno const uint16_t *); 284303628Ssbrunostatic void iwm_set_radio_cfg(const struct iwm_softc *, 285303628Ssbruno struct iwm_nvm_data *, uint32_t); 286286441Srpaulostatic int iwm_parse_nvm_sections(struct iwm_softc *, 287286441Srpaulo struct iwm_nvm_section *); 288286441Srpaulostatic int iwm_nvm_init(struct iwm_softc *); 289303628Ssbrunostatic int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, 290303628Ssbruno const uint8_t *, uint32_t); 291286441Srpaulostatic int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, 292286441Srpaulo const uint8_t *, uint32_t); 293303628Ssbrunostatic int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); 294303628Ssbrunostatic int iwm_load_cpu_sections_8000(struct iwm_softc *, 295303628Ssbruno struct iwm_fw_sects *, int , int *); 296303628Ssbrunostatic int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); 297286441Srpaulostatic int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); 298286441Srpaulostatic int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); 299286441Srpaulostatic int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); 300286441Srpaulostatic int iwm_send_phy_cfg_cmd(struct iwm_softc *); 301286441Srpaulostatic int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, 302286441Srpaulo enum iwm_ucode_type); 303286441Srpaulostatic int iwm_run_init_mvm_ucode(struct iwm_softc *, int); 304286441Srpaulostatic int iwm_rx_addbuf(struct iwm_softc *, int, int); 305286441Srpaulostatic int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); 306286441Srpaulostatic int iwm_mvm_get_signal_strength(struct iwm_softc *, 307286441Srpaulo struct iwm_rx_phy_info *); 308286441Srpaulostatic void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, 309286441Srpaulo struct iwm_rx_packet *, 310286441Srpaulo struct iwm_rx_data *); 311330144Seadlerstatic int iwm_get_noise(struct iwm_softc *sc, 312330144Seadler const struct iwm_mvm_statistics_rx_non_phy *); 313286441Srpaulostatic void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, 314286441Srpaulo struct iwm_rx_data *); 315293100Savosstatic int iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, 316286441Srpaulo struct iwm_rx_packet *, 317286441Srpaulo struct iwm_node *); 318286441Srpaulostatic void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, 319286441Srpaulo struct iwm_rx_data *); 320286441Srpaulostatic void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); 321286441Srpaulo#if 0 322286441Srpaulostatic void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, 323286441Srpaulo uint16_t); 324286441Srpaulo#endif 325286441Srpaulostatic const struct iwm_rate * 326286441Srpaulo iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, 327330155Seadler struct mbuf *, struct iwm_tx_cmd *); 328286441Srpaulostatic int iwm_tx(struct iwm_softc *, struct mbuf *, 329286441Srpaulo struct ieee80211_node *, int); 330286441Srpaulostatic int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, 331286441Srpaulo const struct ieee80211_bpf_params *); 332330154Seadlerstatic int iwm_mvm_flush_tx_path(struct iwm_softc *sc, 333330154Seadler uint32_t tfd_msk, uint32_t flags); 334286441Srpaulostatic int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, 335303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 *, 336286441Srpaulo int *); 337286441Srpaulostatic int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, 338286441Srpaulo int); 339286441Srpaulostatic int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); 340286441Srpaulostatic int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); 341286441Srpaulostatic int iwm_mvm_add_int_sta_common(struct iwm_softc *, 342286441Srpaulo struct iwm_int_sta *, 343286441Srpaulo const uint8_t *, uint16_t, uint16_t); 344286441Srpaulostatic int iwm_mvm_add_aux_sta(struct iwm_softc *); 345286441Srpaulostatic int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *); 346286441Srpaulostatic int iwm_auth(struct ieee80211vap *, struct iwm_softc *); 347286441Srpaulostatic int iwm_assoc(struct ieee80211vap *, struct iwm_softc *); 348286441Srpaulostatic int iwm_release(struct iwm_softc *, struct iwm_node *); 349286441Srpaulostatic struct ieee80211_node * 350286441Srpaulo iwm_node_alloc(struct ieee80211vap *, 351286441Srpaulo const uint8_t[IEEE80211_ADDR_LEN]); 352286441Srpaulostatic void iwm_setrates(struct iwm_softc *, struct iwm_node *); 353286441Srpaulostatic int iwm_media_change(struct ifnet *); 354286441Srpaulostatic int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); 355286441Srpaulostatic void iwm_endscan_cb(void *, int); 356303628Ssbrunostatic void iwm_mvm_fill_sf_command(struct iwm_softc *, 357303628Ssbruno struct iwm_sf_cfg_cmd *, 358303628Ssbruno struct ieee80211_node *); 359303628Ssbrunostatic int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); 360303628Ssbrunostatic int iwm_send_bt_init_conf(struct iwm_softc *); 361303628Ssbrunostatic int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); 362303628Ssbrunostatic void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); 363286441Srpaulostatic int iwm_init_hw(struct iwm_softc *); 364287197Sglebiusstatic void iwm_init(struct iwm_softc *); 365287197Sglebiusstatic void iwm_start(struct iwm_softc *); 366287197Sglebiusstatic void iwm_stop(struct iwm_softc *); 367286441Srpaulostatic void iwm_watchdog(void *); 368287197Sglebiusstatic void iwm_parent(struct ieee80211com *); 369286441Srpaulo#ifdef IWM_DEBUG 370286441Srpaulostatic const char * 371286441Srpaulo iwm_desc_lookup(uint32_t); 372286441Srpaulostatic void iwm_nic_error(struct iwm_softc *); 373303628Ssbrunostatic void iwm_nic_umac_error(struct iwm_softc *); 374286441Srpaulo#endif 375286441Srpaulostatic void iwm_notif_intr(struct iwm_softc *); 376286441Srpaulostatic void iwm_intr(void *); 377286441Srpaulostatic int iwm_attach(device_t); 378303628Ssbrunostatic int iwm_is_valid_ether_addr(uint8_t *); 379286441Srpaulostatic void iwm_preinit(void *); 380286441Srpaulostatic int iwm_detach_local(struct iwm_softc *sc, int); 381286441Srpaulostatic void iwm_init_task(void *); 382286441Srpaulostatic void iwm_radiotap_attach(struct iwm_softc *); 383286441Srpaulostatic struct ieee80211vap * 384286441Srpaulo iwm_vap_create(struct ieee80211com *, 385286441Srpaulo const char [IFNAMSIZ], int, 386286441Srpaulo enum ieee80211_opmode, int, 387286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 388286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN]); 389286441Srpaulostatic void iwm_vap_delete(struct ieee80211vap *); 390286441Srpaulostatic void iwm_scan_start(struct ieee80211com *); 391286441Srpaulostatic void iwm_scan_end(struct ieee80211com *); 392286441Srpaulostatic void iwm_update_mcast(struct ieee80211com *); 393286441Srpaulostatic void iwm_set_channel(struct ieee80211com *); 394286441Srpaulostatic void iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long); 395286441Srpaulostatic void iwm_scan_mindwell(struct ieee80211_scan_state *); 396286441Srpaulostatic int iwm_detach(device_t); 397286441Srpaulo 398286441Srpaulo/* 399286441Srpaulo * Firmware parser. 400286441Srpaulo */ 401286441Srpaulo 402286441Srpaulostatic int 403286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen) 404286441Srpaulo{ 405286441Srpaulo const struct iwm_fw_cscheme_list *l = (const void *)data; 406286441Srpaulo 407286441Srpaulo if (dlen < sizeof(*l) || 408286441Srpaulo dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) 409286441Srpaulo return EINVAL; 410286441Srpaulo 411286441Srpaulo /* we don't actually store anything for now, always use s/w crypto */ 412286441Srpaulo 413286441Srpaulo return 0; 414286441Srpaulo} 415286441Srpaulo 416286441Srpaulostatic int 417286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc, 418286441Srpaulo enum iwm_ucode_type type, const uint8_t *data, size_t dlen) 419286441Srpaulo{ 420286441Srpaulo struct iwm_fw_sects *fws; 421286441Srpaulo struct iwm_fw_onesect *fwone; 422286441Srpaulo 423286441Srpaulo if (type >= IWM_UCODE_TYPE_MAX) 424286441Srpaulo return EINVAL; 425286441Srpaulo if (dlen < sizeof(uint32_t)) 426286441Srpaulo return EINVAL; 427286441Srpaulo 428286441Srpaulo fws = &sc->sc_fw.fw_sects[type]; 429286441Srpaulo if (fws->fw_count >= IWM_UCODE_SECT_MAX) 430286441Srpaulo return EINVAL; 431286441Srpaulo 432286441Srpaulo fwone = &fws->fw_sect[fws->fw_count]; 433286441Srpaulo 434286441Srpaulo /* first 32bit are device load offset */ 435286441Srpaulo memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); 436286441Srpaulo 437286441Srpaulo /* rest is data */ 438286441Srpaulo fwone->fws_data = data + sizeof(uint32_t); 439286441Srpaulo fwone->fws_len = dlen - sizeof(uint32_t); 440286441Srpaulo 441286441Srpaulo fws->fw_count++; 442286441Srpaulo 443286441Srpaulo return 0; 444286441Srpaulo} 445286441Srpaulo 446330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40 447330152Seadler 448286441Srpaulo/* iwlwifi: iwl-drv.c */ 449286441Srpaulostruct iwm_tlv_calib_data { 450286441Srpaulo uint32_t ucode_type; 451286441Srpaulo struct iwm_tlv_calib_ctrl calib; 452286441Srpaulo} __packed; 453286441Srpaulo 454286441Srpaulostatic int 455286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data) 456286441Srpaulo{ 457286441Srpaulo const struct iwm_tlv_calib_data *def_calib = data; 458286441Srpaulo uint32_t ucode_type = le32toh(def_calib->ucode_type); 459286441Srpaulo 460286441Srpaulo if (ucode_type >= IWM_UCODE_TYPE_MAX) { 461286441Srpaulo device_printf(sc->sc_dev, 462286441Srpaulo "Wrong ucode_type %u for default " 463286441Srpaulo "calibration.\n", ucode_type); 464286441Srpaulo return EINVAL; 465286441Srpaulo } 466286441Srpaulo 467286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger = 468286441Srpaulo def_calib->calib.flow_trigger; 469286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger = 470286441Srpaulo def_calib->calib.event_trigger; 471286441Srpaulo 472286441Srpaulo return 0; 473286441Srpaulo} 474286441Srpaulo 475286441Srpaulostatic void 476286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw) 477286441Srpaulo{ 478293177Savos firmware_put(fw->fw_fp, FIRMWARE_UNLOAD); 479293177Savos fw->fw_fp = NULL; 480286441Srpaulo /* don't touch fw->fw_status */ 481286441Srpaulo memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); 482286441Srpaulo} 483286441Srpaulo 484286441Srpaulostatic int 485286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 486286441Srpaulo{ 487286441Srpaulo struct iwm_fw_info *fw = &sc->sc_fw; 488286441Srpaulo const struct iwm_tlv_ucode_header *uhdr; 489286441Srpaulo struct iwm_ucode_tlv tlv; 490286441Srpaulo enum iwm_ucode_tlv_type tlv_type; 491286441Srpaulo const struct firmware *fwp; 492286441Srpaulo const uint8_t *data; 493286441Srpaulo int error = 0; 494286441Srpaulo size_t len; 495286441Srpaulo 496286441Srpaulo if (fw->fw_status == IWM_FW_STATUS_DONE && 497286441Srpaulo ucode_type != IWM_UCODE_TYPE_INIT) 498286441Srpaulo return 0; 499286441Srpaulo 500286441Srpaulo while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) 501286441Srpaulo msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); 502286441Srpaulo fw->fw_status = IWM_FW_STATUS_INPROGRESS; 503286441Srpaulo 504293177Savos if (fw->fw_fp != NULL) 505286441Srpaulo iwm_fw_info_free(fw); 506286441Srpaulo 507286441Srpaulo /* 508286441Srpaulo * Load firmware into driver memory. 509293177Savos * fw_fp will be set. 510286441Srpaulo */ 511286441Srpaulo IWM_UNLOCK(sc); 512286441Srpaulo fwp = firmware_get(sc->sc_fwname); 513293177Savos IWM_LOCK(sc); 514286441Srpaulo if (fwp == NULL) { 515286441Srpaulo device_printf(sc->sc_dev, 516286441Srpaulo "could not read firmware %s (error %d)\n", 517286441Srpaulo sc->sc_fwname, error); 518286441Srpaulo goto out; 519286441Srpaulo } 520293177Savos fw->fw_fp = fwp; 521286441Srpaulo 522303628Ssbruno /* (Re-)Initialize default values. */ 523303628Ssbruno sc->sc_capaflags = 0; 524330152Seadler sc->sc_capa_n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS; 525303628Ssbruno memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); 526303628Ssbruno memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); 527303628Ssbruno 528286441Srpaulo /* 529286441Srpaulo * Parse firmware contents 530286441Srpaulo */ 531286441Srpaulo 532293177Savos uhdr = (const void *)fw->fw_fp->data; 533293177Savos if (*(const uint32_t *)fw->fw_fp->data != 0 534286441Srpaulo || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { 535286441Srpaulo device_printf(sc->sc_dev, "invalid firmware %s\n", 536286441Srpaulo sc->sc_fwname); 537286441Srpaulo error = EINVAL; 538286441Srpaulo goto out; 539286441Srpaulo } 540286441Srpaulo 541303628Ssbruno snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", 542303628Ssbruno IWM_UCODE_MAJOR(le32toh(uhdr->ver)), 543303628Ssbruno IWM_UCODE_MINOR(le32toh(uhdr->ver)), 544303628Ssbruno IWM_UCODE_API(le32toh(uhdr->ver))); 545286441Srpaulo data = uhdr->data; 546293177Savos len = fw->fw_fp->datasize - sizeof(*uhdr); 547286441Srpaulo 548286441Srpaulo while (len >= sizeof(tlv)) { 549286441Srpaulo size_t tlv_len; 550286441Srpaulo const void *tlv_data; 551286441Srpaulo 552286441Srpaulo memcpy(&tlv, data, sizeof(tlv)); 553286441Srpaulo tlv_len = le32toh(tlv.length); 554286441Srpaulo tlv_type = le32toh(tlv.type); 555286441Srpaulo 556286441Srpaulo len -= sizeof(tlv); 557286441Srpaulo data += sizeof(tlv); 558286441Srpaulo tlv_data = data; 559286441Srpaulo 560286441Srpaulo if (len < tlv_len) { 561286441Srpaulo device_printf(sc->sc_dev, 562286441Srpaulo "firmware too short: %zu bytes\n", 563286441Srpaulo len); 564286441Srpaulo error = EINVAL; 565286441Srpaulo goto parse_out; 566286441Srpaulo } 567286441Srpaulo 568286441Srpaulo switch ((int)tlv_type) { 569286441Srpaulo case IWM_UCODE_TLV_PROBE_MAX_LEN: 570286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 571286441Srpaulo device_printf(sc->sc_dev, 572286441Srpaulo "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n", 573286441Srpaulo __func__, 574286441Srpaulo (int) tlv_len); 575286441Srpaulo error = EINVAL; 576286441Srpaulo goto parse_out; 577286441Srpaulo } 578286441Srpaulo sc->sc_capa_max_probe_len 579286441Srpaulo = le32toh(*(const uint32_t *)tlv_data); 580286441Srpaulo /* limit it to something sensible */ 581303628Ssbruno if (sc->sc_capa_max_probe_len > 582303628Ssbruno IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { 583286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 584286441Srpaulo "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " 585286441Srpaulo "ridiculous\n", __func__); 586286441Srpaulo error = EINVAL; 587286441Srpaulo goto parse_out; 588286441Srpaulo } 589286441Srpaulo break; 590286441Srpaulo case IWM_UCODE_TLV_PAN: 591286441Srpaulo if (tlv_len) { 592286441Srpaulo device_printf(sc->sc_dev, 593286441Srpaulo "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", 594286441Srpaulo __func__, 595286441Srpaulo (int) tlv_len); 596286441Srpaulo error = EINVAL; 597286441Srpaulo goto parse_out; 598286441Srpaulo } 599286441Srpaulo sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; 600286441Srpaulo break; 601286441Srpaulo case IWM_UCODE_TLV_FLAGS: 602286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 603286441Srpaulo device_printf(sc->sc_dev, 604286441Srpaulo "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", 605286441Srpaulo __func__, 606286441Srpaulo (int) tlv_len); 607286441Srpaulo error = EINVAL; 608286441Srpaulo goto parse_out; 609286441Srpaulo } 610286441Srpaulo /* 611286441Srpaulo * Apparently there can be many flags, but Linux driver 612286441Srpaulo * parses only the first one, and so do we. 613286441Srpaulo * 614286441Srpaulo * XXX: why does this override IWM_UCODE_TLV_PAN? 615286441Srpaulo * Intentional or a bug? Observations from 616286441Srpaulo * current firmware file: 617286441Srpaulo * 1) TLV_PAN is parsed first 618286441Srpaulo * 2) TLV_FLAGS contains TLV_FLAGS_PAN 619286441Srpaulo * ==> this resets TLV_PAN to itself... hnnnk 620286441Srpaulo */ 621286441Srpaulo sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data); 622286441Srpaulo break; 623286441Srpaulo case IWM_UCODE_TLV_CSCHEME: 624286441Srpaulo if ((error = iwm_store_cscheme(sc, 625286441Srpaulo tlv_data, tlv_len)) != 0) { 626286441Srpaulo device_printf(sc->sc_dev, 627286441Srpaulo "%s: iwm_store_cscheme(): returned %d\n", 628286441Srpaulo __func__, 629286441Srpaulo error); 630286441Srpaulo goto parse_out; 631286441Srpaulo } 632286441Srpaulo break; 633303628Ssbruno case IWM_UCODE_TLV_NUM_OF_CPU: { 634303628Ssbruno uint32_t num_cpu; 635286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 636286441Srpaulo device_printf(sc->sc_dev, 637286441Srpaulo "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", 638286441Srpaulo __func__, 639286441Srpaulo (int) tlv_len); 640286441Srpaulo error = EINVAL; 641286441Srpaulo goto parse_out; 642286441Srpaulo } 643303628Ssbruno num_cpu = le32toh(*(const uint32_t *)tlv_data); 644303628Ssbruno if (num_cpu < 1 || num_cpu > 2) { 645286441Srpaulo device_printf(sc->sc_dev, 646303628Ssbruno "%s: Driver supports only 1 or 2 CPUs\n", 647286441Srpaulo __func__); 648286441Srpaulo error = EINVAL; 649286441Srpaulo goto parse_out; 650286441Srpaulo } 651286441Srpaulo break; 652303628Ssbruno } 653286441Srpaulo case IWM_UCODE_TLV_SEC_RT: 654286441Srpaulo if ((error = iwm_firmware_store_section(sc, 655286441Srpaulo IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) { 656286441Srpaulo device_printf(sc->sc_dev, 657286441Srpaulo "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n", 658286441Srpaulo __func__, 659286441Srpaulo error); 660286441Srpaulo goto parse_out; 661286441Srpaulo } 662286441Srpaulo break; 663286441Srpaulo case IWM_UCODE_TLV_SEC_INIT: 664286441Srpaulo if ((error = iwm_firmware_store_section(sc, 665286441Srpaulo IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) { 666286441Srpaulo device_printf(sc->sc_dev, 667286441Srpaulo "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n", 668286441Srpaulo __func__, 669286441Srpaulo error); 670286441Srpaulo goto parse_out; 671286441Srpaulo } 672286441Srpaulo break; 673286441Srpaulo case IWM_UCODE_TLV_SEC_WOWLAN: 674286441Srpaulo if ((error = iwm_firmware_store_section(sc, 675286441Srpaulo IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) { 676286441Srpaulo device_printf(sc->sc_dev, 677286441Srpaulo "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n", 678286441Srpaulo __func__, 679286441Srpaulo error); 680286441Srpaulo goto parse_out; 681286441Srpaulo } 682286441Srpaulo break; 683286441Srpaulo case IWM_UCODE_TLV_DEF_CALIB: 684286441Srpaulo if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { 685286441Srpaulo device_printf(sc->sc_dev, 686286441Srpaulo "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", 687286441Srpaulo __func__, 688286441Srpaulo (int) tlv_len, 689286441Srpaulo (int) sizeof(struct iwm_tlv_calib_data)); 690286441Srpaulo error = EINVAL; 691286441Srpaulo goto parse_out; 692286441Srpaulo } 693286441Srpaulo if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { 694286441Srpaulo device_printf(sc->sc_dev, 695286441Srpaulo "%s: iwm_set_default_calib() failed: %d\n", 696286441Srpaulo __func__, 697286441Srpaulo error); 698286441Srpaulo goto parse_out; 699286441Srpaulo } 700286441Srpaulo break; 701286441Srpaulo case IWM_UCODE_TLV_PHY_SKU: 702286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 703286441Srpaulo error = EINVAL; 704286441Srpaulo device_printf(sc->sc_dev, 705286441Srpaulo "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", 706286441Srpaulo __func__, 707286441Srpaulo (int) tlv_len); 708286441Srpaulo goto parse_out; 709286441Srpaulo } 710286441Srpaulo sc->sc_fw_phy_config = 711286441Srpaulo le32toh(*(const uint32_t *)tlv_data); 712286441Srpaulo break; 713286441Srpaulo 714303628Ssbruno case IWM_UCODE_TLV_API_CHANGES_SET: { 715303628Ssbruno const struct iwm_ucode_api *api; 716303628Ssbruno if (tlv_len != sizeof(*api)) { 717303628Ssbruno error = EINVAL; 718303628Ssbruno goto parse_out; 719303628Ssbruno } 720303628Ssbruno api = (const struct iwm_ucode_api *)tlv_data; 721303628Ssbruno /* Flags may exceed 32 bits in future firmware. */ 722303628Ssbruno if (le32toh(api->api_index) > 0) { 723303628Ssbruno device_printf(sc->sc_dev, 724303628Ssbruno "unsupported API index %d\n", 725303628Ssbruno le32toh(api->api_index)); 726303628Ssbruno goto parse_out; 727303628Ssbruno } 728303628Ssbruno sc->sc_ucode_api = le32toh(api->api_flags); 729303628Ssbruno break; 730303628Ssbruno } 731303628Ssbruno 732303628Ssbruno case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { 733303628Ssbruno const struct iwm_ucode_capa *capa; 734303628Ssbruno int idx, i; 735303628Ssbruno if (tlv_len != sizeof(*capa)) { 736303628Ssbruno error = EINVAL; 737303628Ssbruno goto parse_out; 738303628Ssbruno } 739303628Ssbruno capa = (const struct iwm_ucode_capa *)tlv_data; 740303628Ssbruno idx = le32toh(capa->api_index); 741330148Seadler if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { 742303628Ssbruno device_printf(sc->sc_dev, 743303628Ssbruno "unsupported API index %d\n", idx); 744303628Ssbruno goto parse_out; 745303628Ssbruno } 746303628Ssbruno for (i = 0; i < 32; i++) { 747303628Ssbruno if ((le32toh(capa->api_capa) & (1U << i)) == 0) 748303628Ssbruno continue; 749303628Ssbruno setbit(sc->sc_enabled_capa, i + (32 * idx)); 750303628Ssbruno } 751303628Ssbruno break; 752303628Ssbruno } 753303628Ssbruno 754303628Ssbruno case 48: /* undocumented TLV */ 755303628Ssbruno case IWM_UCODE_TLV_SDIO_ADMA_ADDR: 756303628Ssbruno case IWM_UCODE_TLV_FW_GSCAN_CAPA: 757286441Srpaulo /* ignore, not used by current driver */ 758286441Srpaulo break; 759286441Srpaulo 760303628Ssbruno case IWM_UCODE_TLV_SEC_RT_USNIFFER: 761303628Ssbruno if ((error = iwm_firmware_store_section(sc, 762303628Ssbruno IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, 763303628Ssbruno tlv_len)) != 0) 764303628Ssbruno goto parse_out; 765303628Ssbruno break; 766303628Ssbruno 767303628Ssbruno case IWM_UCODE_TLV_N_SCAN_CHANNELS: 768303628Ssbruno if (tlv_len != sizeof(uint32_t)) { 769303628Ssbruno error = EINVAL; 770303628Ssbruno goto parse_out; 771303628Ssbruno } 772303628Ssbruno sc->sc_capa_n_scan_channels = 773303628Ssbruno le32toh(*(const uint32_t *)tlv_data); 774303628Ssbruno break; 775303628Ssbruno 776303628Ssbruno case IWM_UCODE_TLV_FW_VERSION: 777303628Ssbruno if (tlv_len != sizeof(uint32_t) * 3) { 778303628Ssbruno error = EINVAL; 779303628Ssbruno goto parse_out; 780303628Ssbruno } 781303628Ssbruno snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), 782303628Ssbruno "%d.%d.%d", 783303628Ssbruno le32toh(((const uint32_t *)tlv_data)[0]), 784303628Ssbruno le32toh(((const uint32_t *)tlv_data)[1]), 785303628Ssbruno le32toh(((const uint32_t *)tlv_data)[2])); 786303628Ssbruno break; 787303628Ssbruno 788286441Srpaulo default: 789286441Srpaulo device_printf(sc->sc_dev, 790286441Srpaulo "%s: unknown firmware section %d, abort\n", 791286441Srpaulo __func__, tlv_type); 792286441Srpaulo error = EINVAL; 793286441Srpaulo goto parse_out; 794286441Srpaulo } 795286441Srpaulo 796286441Srpaulo len -= roundup(tlv_len, 4); 797286441Srpaulo data += roundup(tlv_len, 4); 798286441Srpaulo } 799286441Srpaulo 800286441Srpaulo KASSERT(error == 0, ("unhandled error")); 801286441Srpaulo 802286441Srpaulo parse_out: 803286441Srpaulo if (error) { 804286441Srpaulo device_printf(sc->sc_dev, "firmware parse error %d, " 805286441Srpaulo "section type %d\n", error, tlv_type); 806286441Srpaulo } 807286441Srpaulo 808286441Srpaulo if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { 809286441Srpaulo device_printf(sc->sc_dev, 810286441Srpaulo "device uses unsupported power ops\n"); 811286441Srpaulo error = ENOTSUP; 812286441Srpaulo } 813286441Srpaulo 814286441Srpaulo out: 815286441Srpaulo if (error) { 816286441Srpaulo fw->fw_status = IWM_FW_STATUS_NONE; 817293177Savos if (fw->fw_fp != NULL) 818286441Srpaulo iwm_fw_info_free(fw); 819286441Srpaulo } else 820286441Srpaulo fw->fw_status = IWM_FW_STATUS_DONE; 821286441Srpaulo wakeup(&sc->sc_fw); 822286441Srpaulo 823286441Srpaulo return error; 824286441Srpaulo} 825286441Srpaulo 826286441Srpaulo/* 827286441Srpaulo * DMA resource routines 828286441Srpaulo */ 829286441Srpaulo 830286441Srpaulostatic void 831286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 832286441Srpaulo{ 833286441Srpaulo if (error != 0) 834286441Srpaulo return; 835286441Srpaulo KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 836303628Ssbruno *(bus_addr_t *)arg = segs[0].ds_addr; 837286441Srpaulo} 838286441Srpaulo 839286441Srpaulostatic int 840286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, 841286441Srpaulo bus_size_t size, bus_size_t alignment) 842286441Srpaulo{ 843286441Srpaulo int error; 844286441Srpaulo 845286441Srpaulo dma->tag = NULL; 846303628Ssbruno dma->map = NULL; 847286441Srpaulo dma->size = size; 848302102Sadrian dma->vaddr = NULL; 849286441Srpaulo 850286441Srpaulo error = bus_dma_tag_create(tag, alignment, 851286441Srpaulo 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 852289679Skevlo 1, size, 0, NULL, NULL, &dma->tag); 853286441Srpaulo if (error != 0) 854286441Srpaulo goto fail; 855286441Srpaulo 856286441Srpaulo error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, 857286441Srpaulo BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); 858286441Srpaulo if (error != 0) 859286441Srpaulo goto fail; 860286441Srpaulo 861286441Srpaulo error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, 862286441Srpaulo iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); 863302102Sadrian if (error != 0) { 864302102Sadrian bus_dmamem_free(dma->tag, dma->vaddr, dma->map); 865302102Sadrian dma->vaddr = NULL; 866303628Ssbruno goto fail; 867302102Sadrian } 868286441Srpaulo 869286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 870286441Srpaulo 871286441Srpaulo return 0; 872286441Srpaulo 873303628Ssbrunofail: 874303628Ssbruno iwm_dma_contig_free(dma); 875303628Ssbruno 876286441Srpaulo return error; 877286441Srpaulo} 878286441Srpaulo 879286441Srpaulostatic void 880286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma) 881286441Srpaulo{ 882302102Sadrian if (dma->vaddr != NULL) { 883302102Sadrian bus_dmamap_sync(dma->tag, dma->map, 884302102Sadrian BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 885302102Sadrian bus_dmamap_unload(dma->tag, dma->map); 886302102Sadrian bus_dmamem_free(dma->tag, dma->vaddr, dma->map); 887302102Sadrian dma->vaddr = NULL; 888286441Srpaulo } 889286441Srpaulo if (dma->tag != NULL) { 890286441Srpaulo bus_dma_tag_destroy(dma->tag); 891286441Srpaulo dma->tag = NULL; 892286441Srpaulo } 893286441Srpaulo} 894286441Srpaulo 895286441Srpaulo/* fwmem is used to load firmware onto the card */ 896286441Srpaulostatic int 897286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc) 898286441Srpaulo{ 899286441Srpaulo /* Must be aligned on a 16-byte boundary. */ 900286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, 901286441Srpaulo sc->sc_fwdmasegsz, 16); 902286441Srpaulo} 903286441Srpaulo 904286441Srpaulo/* tx scheduler rings. not used? */ 905286441Srpaulostatic int 906286441Srpauloiwm_alloc_sched(struct iwm_softc *sc) 907286441Srpaulo{ 908286441Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 909303628Ssbruno return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, 910286441Srpaulo nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); 911286441Srpaulo} 912286441Srpaulo 913286441Srpaulo/* keep-warm page is used internally by the card. see iwl-fh.h for more info */ 914286441Srpaulostatic int 915286441Srpauloiwm_alloc_kw(struct iwm_softc *sc) 916286441Srpaulo{ 917286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); 918286441Srpaulo} 919286441Srpaulo 920286441Srpaulo/* interrupt cause table */ 921286441Srpaulostatic int 922286441Srpauloiwm_alloc_ict(struct iwm_softc *sc) 923286441Srpaulo{ 924286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, 925286441Srpaulo IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); 926286441Srpaulo} 927286441Srpaulo 928286441Srpaulostatic int 929286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 930286441Srpaulo{ 931286441Srpaulo bus_size_t size; 932286441Srpaulo int i, error; 933286441Srpaulo 934286441Srpaulo ring->cur = 0; 935286441Srpaulo 936286441Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 937286441Srpaulo size = IWM_RX_RING_COUNT * sizeof(uint32_t); 938286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 939286441Srpaulo if (error != 0) { 940286441Srpaulo device_printf(sc->sc_dev, 941286441Srpaulo "could not allocate RX ring DMA memory\n"); 942286441Srpaulo goto fail; 943286441Srpaulo } 944286441Srpaulo ring->desc = ring->desc_dma.vaddr; 945286441Srpaulo 946286441Srpaulo /* Allocate RX status area (16-byte aligned). */ 947286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, 948286441Srpaulo sizeof(*ring->stat), 16); 949286441Srpaulo if (error != 0) { 950286441Srpaulo device_printf(sc->sc_dev, 951286441Srpaulo "could not allocate RX status DMA memory\n"); 952286441Srpaulo goto fail; 953286441Srpaulo } 954286441Srpaulo ring->stat = ring->stat_dma.vaddr; 955286441Srpaulo 956286441Srpaulo /* Create RX buffer DMA tag. */ 957286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 958286441Srpaulo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 959289679Skevlo IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat); 960286441Srpaulo if (error != 0) { 961286441Srpaulo device_printf(sc->sc_dev, 962286441Srpaulo "%s: could not create RX buf DMA tag, error %d\n", 963286441Srpaulo __func__, error); 964286441Srpaulo goto fail; 965286441Srpaulo } 966286441Srpaulo 967301845Sadrian /* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */ 968301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map); 969301845Sadrian if (error != 0) { 970301845Sadrian device_printf(sc->sc_dev, 971301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 972301845Sadrian __func__, error); 973301845Sadrian goto fail; 974301845Sadrian } 975286441Srpaulo /* 976286441Srpaulo * Allocate and map RX buffers. 977286441Srpaulo */ 978286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 979301845Sadrian struct iwm_rx_data *data = &ring->data[i]; 980301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 981301845Sadrian if (error != 0) { 982301845Sadrian device_printf(sc->sc_dev, 983301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 984301845Sadrian __func__, error); 985301845Sadrian goto fail; 986301845Sadrian } 987301845Sadrian data->m = NULL; 988301845Sadrian 989286441Srpaulo if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) { 990286441Srpaulo goto fail; 991286441Srpaulo } 992286441Srpaulo } 993286441Srpaulo return 0; 994286441Srpaulo 995286441Srpaulofail: iwm_free_rx_ring(sc, ring); 996286441Srpaulo return error; 997286441Srpaulo} 998286441Srpaulo 999286441Srpaulostatic void 1000301191Sadrianiwm_disable_rx_dma(struct iwm_softc *sc) 1001286441Srpaulo{ 1002303628Ssbruno /* XXX conditional nic locks are stupid */ 1003286441Srpaulo /* XXX print out if we can't lock the NIC? */ 1004286441Srpaulo if (iwm_nic_lock(sc)) { 1005286441Srpaulo /* XXX handle if RX stop doesn't finish? */ 1006286441Srpaulo (void) iwm_pcie_rx_stop(sc); 1007286441Srpaulo iwm_nic_unlock(sc); 1008286441Srpaulo } 1009301191Sadrian} 1010301191Sadrian 1011301191Sadrianstatic void 1012301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1013301191Sadrian{ 1014287965Sadrian /* Reset the ring state */ 1015286441Srpaulo ring->cur = 0; 1016303628Ssbruno 1017303628Ssbruno /* 1018303628Ssbruno * The hw rx ring index in shared memory must also be cleared, 1019303628Ssbruno * otherwise the discrepancy can cause reprocessing chaos. 1020303628Ssbruno */ 1021287965Sadrian memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1022286441Srpaulo} 1023286441Srpaulo 1024286441Srpaulostatic void 1025286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1026286441Srpaulo{ 1027286441Srpaulo int i; 1028286441Srpaulo 1029286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1030286441Srpaulo iwm_dma_contig_free(&ring->stat_dma); 1031286441Srpaulo 1032286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 1033286441Srpaulo struct iwm_rx_data *data = &ring->data[i]; 1034286441Srpaulo 1035286441Srpaulo if (data->m != NULL) { 1036286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1037286441Srpaulo BUS_DMASYNC_POSTREAD); 1038286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1039286441Srpaulo m_freem(data->m); 1040286441Srpaulo data->m = NULL; 1041286441Srpaulo } 1042286441Srpaulo if (data->map != NULL) { 1043286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1044286441Srpaulo data->map = NULL; 1045286441Srpaulo } 1046286441Srpaulo } 1047301845Sadrian if (ring->spare_map != NULL) { 1048301845Sadrian bus_dmamap_destroy(ring->data_dmat, ring->spare_map); 1049301845Sadrian ring->spare_map = NULL; 1050301845Sadrian } 1051286441Srpaulo if (ring->data_dmat != NULL) { 1052286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1053286441Srpaulo ring->data_dmat = NULL; 1054286441Srpaulo } 1055286441Srpaulo} 1056286441Srpaulo 1057286441Srpaulostatic int 1058286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) 1059286441Srpaulo{ 1060286441Srpaulo bus_addr_t paddr; 1061286441Srpaulo bus_size_t size; 1062302104Sadrian size_t maxsize; 1063302104Sadrian int nsegments; 1064286441Srpaulo int i, error; 1065286441Srpaulo 1066286441Srpaulo ring->qid = qid; 1067286441Srpaulo ring->queued = 0; 1068286441Srpaulo ring->cur = 0; 1069286441Srpaulo 1070286441Srpaulo /* Allocate TX descriptors (256-byte aligned). */ 1071286441Srpaulo size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd); 1072286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 1073286441Srpaulo if (error != 0) { 1074286441Srpaulo device_printf(sc->sc_dev, 1075286441Srpaulo "could not allocate TX ring DMA memory\n"); 1076286441Srpaulo goto fail; 1077286441Srpaulo } 1078286441Srpaulo ring->desc = ring->desc_dma.vaddr; 1079286441Srpaulo 1080286441Srpaulo /* 1081286441Srpaulo * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need 1082286441Srpaulo * to allocate commands space for other rings. 1083286441Srpaulo */ 1084286441Srpaulo if (qid > IWM_MVM_CMD_QUEUE) 1085286441Srpaulo return 0; 1086286441Srpaulo 1087286441Srpaulo size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd); 1088286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4); 1089286441Srpaulo if (error != 0) { 1090286441Srpaulo device_printf(sc->sc_dev, 1091286441Srpaulo "could not allocate TX cmd DMA memory\n"); 1092286441Srpaulo goto fail; 1093286441Srpaulo } 1094286441Srpaulo ring->cmd = ring->cmd_dma.vaddr; 1095286441Srpaulo 1096302104Sadrian /* FW commands may require more mapped space than packets. */ 1097302104Sadrian if (qid == IWM_MVM_CMD_QUEUE) { 1098302104Sadrian maxsize = IWM_RBUF_SIZE; 1099302104Sadrian nsegments = 1; 1100302104Sadrian } else { 1101302104Sadrian maxsize = MCLBYTES; 1102302104Sadrian nsegments = IWM_MAX_SCATTER - 2; 1103302104Sadrian } 1104302104Sadrian 1105286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 1106302104Sadrian BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, 1107302104Sadrian nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat); 1108286441Srpaulo if (error != 0) { 1109286441Srpaulo device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); 1110286441Srpaulo goto fail; 1111286441Srpaulo } 1112286441Srpaulo 1113286441Srpaulo paddr = ring->cmd_dma.paddr; 1114286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1115286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1116286441Srpaulo 1117286441Srpaulo data->cmd_paddr = paddr; 1118286441Srpaulo data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) 1119286441Srpaulo + offsetof(struct iwm_tx_cmd, scratch); 1120286441Srpaulo paddr += sizeof(struct iwm_device_cmd); 1121286441Srpaulo 1122286441Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1123286441Srpaulo if (error != 0) { 1124286441Srpaulo device_printf(sc->sc_dev, 1125286441Srpaulo "could not create TX buf DMA map\n"); 1126286441Srpaulo goto fail; 1127286441Srpaulo } 1128286441Srpaulo } 1129286441Srpaulo KASSERT(paddr == ring->cmd_dma.paddr + size, 1130286441Srpaulo ("invalid physical address")); 1131286441Srpaulo return 0; 1132286441Srpaulo 1133286441Srpaulofail: iwm_free_tx_ring(sc, ring); 1134286441Srpaulo return error; 1135286441Srpaulo} 1136286441Srpaulo 1137286441Srpaulostatic void 1138286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1139286441Srpaulo{ 1140286441Srpaulo int i; 1141286441Srpaulo 1142286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1143286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1144286441Srpaulo 1145286441Srpaulo if (data->m != NULL) { 1146286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1147286441Srpaulo BUS_DMASYNC_POSTWRITE); 1148286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1149286441Srpaulo m_freem(data->m); 1150286441Srpaulo data->m = NULL; 1151286441Srpaulo } 1152286441Srpaulo } 1153286441Srpaulo /* Clear TX descriptors. */ 1154286441Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1155286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1156286441Srpaulo BUS_DMASYNC_PREWRITE); 1157286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1158286441Srpaulo ring->queued = 0; 1159286441Srpaulo ring->cur = 0; 1160286441Srpaulo} 1161286441Srpaulo 1162286441Srpaulostatic void 1163286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1164286441Srpaulo{ 1165286441Srpaulo int i; 1166286441Srpaulo 1167286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1168286441Srpaulo iwm_dma_contig_free(&ring->cmd_dma); 1169286441Srpaulo 1170286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1171286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1172286441Srpaulo 1173286441Srpaulo if (data->m != NULL) { 1174286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1175286441Srpaulo BUS_DMASYNC_POSTWRITE); 1176286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1177286441Srpaulo m_freem(data->m); 1178286441Srpaulo data->m = NULL; 1179286441Srpaulo } 1180286441Srpaulo if (data->map != NULL) { 1181286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1182286441Srpaulo data->map = NULL; 1183286441Srpaulo } 1184286441Srpaulo } 1185286441Srpaulo if (ring->data_dmat != NULL) { 1186286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1187286441Srpaulo ring->data_dmat = NULL; 1188286441Srpaulo } 1189286441Srpaulo} 1190286441Srpaulo 1191286441Srpaulo/* 1192286441Srpaulo * High-level hardware frobbing routines 1193286441Srpaulo */ 1194286441Srpaulo 1195286441Srpaulostatic void 1196286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc) 1197286441Srpaulo{ 1198286441Srpaulo sc->sc_intmask = IWM_CSR_INI_SET_MASK; 1199286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1200286441Srpaulo} 1201286441Srpaulo 1202286441Srpaulostatic void 1203286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc) 1204286441Srpaulo{ 1205286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1206286441Srpaulo} 1207286441Srpaulo 1208286441Srpaulostatic void 1209286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc) 1210286441Srpaulo{ 1211286441Srpaulo /* disable interrupts */ 1212286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 1213286441Srpaulo 1214286441Srpaulo /* acknowledge all interrupts */ 1215286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1216286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0); 1217286441Srpaulo} 1218286441Srpaulo 1219286441Srpaulostatic void 1220286441Srpauloiwm_ict_reset(struct iwm_softc *sc) 1221286441Srpaulo{ 1222286441Srpaulo iwm_disable_interrupts(sc); 1223286441Srpaulo 1224286441Srpaulo /* Reset ICT table. */ 1225286441Srpaulo memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE); 1226286441Srpaulo sc->ict_cur = 0; 1227286441Srpaulo 1228286441Srpaulo /* Set physical address of ICT table (4KB aligned). */ 1229286441Srpaulo IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, 1230286441Srpaulo IWM_CSR_DRAM_INT_TBL_ENABLE 1231303628Ssbruno | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER 1232286441Srpaulo | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK 1233286441Srpaulo | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); 1234286441Srpaulo 1235286441Srpaulo /* Switch to ICT interrupt mode in driver. */ 1236286441Srpaulo sc->sc_flags |= IWM_FLAG_USE_ICT; 1237286441Srpaulo 1238286441Srpaulo /* Re-enable interrupts. */ 1239286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1240286441Srpaulo iwm_enable_interrupts(sc); 1241286441Srpaulo} 1242286441Srpaulo 1243286441Srpaulo/* iwlwifi pcie/trans.c */ 1244286441Srpaulo 1245286441Srpaulo/* 1246286441Srpaulo * Since this .. hard-resets things, it's time to actually 1247286441Srpaulo * mark the first vap (if any) as having no mac context. 1248286441Srpaulo * It's annoying, but since the driver is potentially being 1249286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we 1250286441Srpaulo * have to correctly track this. 1251286441Srpaulo */ 1252286441Srpaulostatic void 1253286441Srpauloiwm_stop_device(struct iwm_softc *sc) 1254286441Srpaulo{ 1255287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1256286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1257303628Ssbruno int chnl, qid; 1258303628Ssbruno uint32_t mask = 0; 1259286441Srpaulo 1260286441Srpaulo /* tell the device to stop sending interrupts */ 1261286441Srpaulo iwm_disable_interrupts(sc); 1262286441Srpaulo 1263286441Srpaulo /* 1264286441Srpaulo * FreeBSD-local: mark the first vap as not-uploaded, 1265286441Srpaulo * so the next transition through auth/assoc 1266286441Srpaulo * will correctly populate the MAC context. 1267286441Srpaulo */ 1268286441Srpaulo if (vap) { 1269286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 1270286441Srpaulo iv->is_uploaded = 0; 1271286441Srpaulo } 1272286441Srpaulo 1273286441Srpaulo /* device going down, Stop using ICT table */ 1274286441Srpaulo sc->sc_flags &= ~IWM_FLAG_USE_ICT; 1275286441Srpaulo 1276286441Srpaulo /* stop tx and rx. tx and rx bits, as usual, are from if_iwn */ 1277286441Srpaulo 1278286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1279286441Srpaulo 1280286441Srpaulo if (iwm_nic_lock(sc)) { 1281303628Ssbruno /* Stop each Tx DMA channel */ 1282286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1283286441Srpaulo IWM_WRITE(sc, 1284286441Srpaulo IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0); 1285303628Ssbruno mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl); 1286303628Ssbruno } 1287286441Srpaulo 1288303628Ssbruno /* Wait for DMA channels to be idle */ 1289303628Ssbruno if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask, 1290303628Ssbruno 5000)) { 1291303628Ssbruno device_printf(sc->sc_dev, 1292303628Ssbruno "Failing on timeout while stopping DMA channel: [0x%08x]\n", 1293303628Ssbruno IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG)); 1294286441Srpaulo } 1295286441Srpaulo iwm_nic_unlock(sc); 1296286441Srpaulo } 1297301191Sadrian iwm_disable_rx_dma(sc); 1298286441Srpaulo 1299286441Srpaulo /* Stop RX ring. */ 1300286441Srpaulo iwm_reset_rx_ring(sc, &sc->rxq); 1301286441Srpaulo 1302286441Srpaulo /* Reset all TX rings. */ 1303286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) 1304286441Srpaulo iwm_reset_tx_ring(sc, &sc->txq[qid]); 1305286441Srpaulo 1306286441Srpaulo /* 1307286441Srpaulo * Power-down device's busmaster DMA clocks 1308286441Srpaulo */ 1309286441Srpaulo iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); 1310286441Srpaulo DELAY(5); 1311286441Srpaulo 1312286441Srpaulo /* Make sure (redundant) we've released our request to stay awake */ 1313286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 1314286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 1315286441Srpaulo 1316286441Srpaulo /* Stop the device, and put it in low power state */ 1317286441Srpaulo iwm_apm_stop(sc); 1318286441Srpaulo 1319286441Srpaulo /* Upon stop, the APM issues an interrupt if HW RF kill is set. 1320286441Srpaulo * Clean again the interrupt here 1321286441Srpaulo */ 1322286441Srpaulo iwm_disable_interrupts(sc); 1323286441Srpaulo /* stop and reset the on-board processor */ 1324303628Ssbruno IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); 1325286441Srpaulo 1326286441Srpaulo /* 1327286441Srpaulo * Even if we stop the HW, we still want the RF kill 1328286441Srpaulo * interrupt 1329286441Srpaulo */ 1330286441Srpaulo iwm_enable_rfkill_int(sc); 1331286441Srpaulo iwm_check_rfkill(sc); 1332286441Srpaulo} 1333286441Srpaulo 1334286441Srpaulo/* iwlwifi: mvm/ops.c */ 1335286441Srpaulostatic void 1336286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc) 1337286441Srpaulo{ 1338286441Srpaulo uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash; 1339286441Srpaulo uint32_t reg_val = 0; 1340286441Srpaulo 1341286441Srpaulo radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >> 1342286441Srpaulo IWM_FW_PHY_CFG_RADIO_TYPE_POS; 1343286441Srpaulo radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >> 1344286441Srpaulo IWM_FW_PHY_CFG_RADIO_STEP_POS; 1345286441Srpaulo radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >> 1346286441Srpaulo IWM_FW_PHY_CFG_RADIO_DASH_POS; 1347286441Srpaulo 1348286441Srpaulo /* SKU control */ 1349286441Srpaulo reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) << 1350286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; 1351286441Srpaulo reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) << 1352286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; 1353286441Srpaulo 1354286441Srpaulo /* radio configuration */ 1355286441Srpaulo reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; 1356286441Srpaulo reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; 1357286441Srpaulo reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; 1358286441Srpaulo 1359286441Srpaulo IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val); 1360286441Srpaulo 1361286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1362286441Srpaulo "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, 1363286441Srpaulo radio_cfg_step, radio_cfg_dash); 1364286441Srpaulo 1365286441Srpaulo /* 1366286441Srpaulo * W/A : NIC is stuck in a reset state after Early PCIe power off 1367286441Srpaulo * (PCIe power is lost before PERST# is asserted), causing ME FW 1368286441Srpaulo * to lose ownership and not being able to obtain it back. 1369286441Srpaulo */ 1370303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { 1371303628Ssbruno iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, 1372303628Ssbruno IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, 1373303628Ssbruno ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); 1374303628Ssbruno } 1375286441Srpaulo} 1376286441Srpaulo 1377286441Srpaulostatic int 1378286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc) 1379286441Srpaulo{ 1380286441Srpaulo if (!iwm_nic_lock(sc)) 1381286441Srpaulo return EBUSY; 1382286441Srpaulo 1383286441Srpaulo /* 1384286441Srpaulo * Initialize RX ring. This is from the iwn driver. 1385286441Srpaulo */ 1386286441Srpaulo memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1387286441Srpaulo 1388286441Srpaulo /* stop DMA */ 1389301191Sadrian iwm_disable_rx_dma(sc); 1390286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); 1391286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); 1392286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0); 1393286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 1394286441Srpaulo 1395286441Srpaulo /* Set physical address of RX ring (256-byte aligned). */ 1396286441Srpaulo IWM_WRITE(sc, 1397286441Srpaulo IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8); 1398286441Srpaulo 1399286441Srpaulo /* Set physical address of RX status (16-byte aligned). */ 1400286441Srpaulo IWM_WRITE(sc, 1401286441Srpaulo IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); 1402286441Srpaulo 1403286441Srpaulo /* Enable RX. */ 1404286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 1405286441Srpaulo IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 1406286441Srpaulo IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ 1407286441Srpaulo IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 1408303628Ssbruno IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | 1409303628Ssbruno (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | 1410286441Srpaulo IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | 1411286441Srpaulo IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); 1412286441Srpaulo 1413286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF); 1414286441Srpaulo 1415286441Srpaulo /* W/A for interrupt coalescing bug in 7260 and 3160 */ 1416286441Srpaulo if (sc->host_interrupt_operation_mode) 1417286441Srpaulo IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE); 1418286441Srpaulo 1419286441Srpaulo /* 1420286441Srpaulo * Thus sayeth el jefe (iwlwifi) via a comment: 1421286441Srpaulo * 1422286441Srpaulo * This value should initially be 0 (before preparing any 1423303628Ssbruno * RBs), should be 8 after preparing the first 8 RBs (for example) 1424286441Srpaulo */ 1425286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); 1426286441Srpaulo 1427286441Srpaulo iwm_nic_unlock(sc); 1428286441Srpaulo 1429286441Srpaulo return 0; 1430286441Srpaulo} 1431286441Srpaulo 1432286441Srpaulostatic int 1433286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc) 1434286441Srpaulo{ 1435286441Srpaulo int qid; 1436286441Srpaulo 1437286441Srpaulo if (!iwm_nic_lock(sc)) 1438286441Srpaulo return EBUSY; 1439286441Srpaulo 1440286441Srpaulo /* Deactivate TX scheduler. */ 1441286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1442286441Srpaulo 1443286441Srpaulo /* Set physical address of "keep warm" page (16-byte aligned). */ 1444286441Srpaulo IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4); 1445286441Srpaulo 1446286441Srpaulo /* Initialize TX rings. */ 1447286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) { 1448286441Srpaulo struct iwm_tx_ring *txq = &sc->txq[qid]; 1449286441Srpaulo 1450286441Srpaulo /* Set physical address of TX ring (256-byte aligned). */ 1451286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid), 1452286441Srpaulo txq->desc_dma.paddr >> 8); 1453286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 1454286441Srpaulo "%s: loading ring %d descriptors (%p) at %lx\n", 1455286441Srpaulo __func__, 1456286441Srpaulo qid, txq->desc, 1457286441Srpaulo (unsigned long) (txq->desc_dma.paddr >> 8)); 1458286441Srpaulo } 1459303628Ssbruno 1460303628Ssbruno iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); 1461303628Ssbruno 1462286441Srpaulo iwm_nic_unlock(sc); 1463286441Srpaulo 1464286441Srpaulo return 0; 1465286441Srpaulo} 1466286441Srpaulo 1467286441Srpaulostatic int 1468286441Srpauloiwm_nic_init(struct iwm_softc *sc) 1469286441Srpaulo{ 1470286441Srpaulo int error; 1471286441Srpaulo 1472286441Srpaulo iwm_apm_init(sc); 1473303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) 1474303628Ssbruno iwm_set_pwr(sc); 1475286441Srpaulo 1476286441Srpaulo iwm_mvm_nic_config(sc); 1477286441Srpaulo 1478286441Srpaulo if ((error = iwm_nic_rx_init(sc)) != 0) 1479286441Srpaulo return error; 1480286441Srpaulo 1481286441Srpaulo /* 1482286441Srpaulo * Ditto for TX, from iwn 1483286441Srpaulo */ 1484286441Srpaulo if ((error = iwm_nic_tx_init(sc)) != 0) 1485286441Srpaulo return error; 1486286441Srpaulo 1487286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1488286441Srpaulo "%s: shadow registers enabled\n", __func__); 1489286441Srpaulo IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff); 1490286441Srpaulo 1491286441Srpaulo return 0; 1492286441Srpaulo} 1493286441Srpaulo 1494286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = { 1495286441Srpaulo IWM_MVM_TX_FIFO_VO, 1496286441Srpaulo IWM_MVM_TX_FIFO_VI, 1497286441Srpaulo IWM_MVM_TX_FIFO_BE, 1498286441Srpaulo IWM_MVM_TX_FIFO_BK, 1499286441Srpaulo}; 1500286441Srpaulo 1501303628Ssbrunostatic int 1502303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) 1503286441Srpaulo{ 1504286441Srpaulo if (!iwm_nic_lock(sc)) { 1505286441Srpaulo device_printf(sc->sc_dev, 1506286441Srpaulo "%s: cannot enable txq %d\n", 1507286441Srpaulo __func__, 1508286441Srpaulo qid); 1509303628Ssbruno return EBUSY; 1510286441Srpaulo } 1511286441Srpaulo 1512303628Ssbruno IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); 1513286441Srpaulo 1514303628Ssbruno if (qid == IWM_MVM_CMD_QUEUE) { 1515303628Ssbruno /* unactivate before configuration */ 1516303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1517303628Ssbruno (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) 1518303628Ssbruno | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); 1519286441Srpaulo 1520303628Ssbruno iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); 1521286441Srpaulo 1522303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); 1523286441Srpaulo 1524303628Ssbruno iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); 1525303628Ssbruno /* Set scheduler window size and frame limit. */ 1526303628Ssbruno iwm_write_mem32(sc, 1527303628Ssbruno sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + 1528303628Ssbruno sizeof(uint32_t), 1529303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & 1530303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | 1531303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 1532303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); 1533286441Srpaulo 1534303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1535303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 1536303628Ssbruno (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | 1537303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | 1538303628Ssbruno IWM_SCD_QUEUE_STTS_REG_MSK); 1539303628Ssbruno } else { 1540303628Ssbruno struct iwm_scd_txq_cfg_cmd cmd; 1541303628Ssbruno int error; 1542286441Srpaulo 1543303628Ssbruno iwm_nic_unlock(sc); 1544303628Ssbruno 1545303628Ssbruno memset(&cmd, 0, sizeof(cmd)); 1546303628Ssbruno cmd.scd_queue = qid; 1547303628Ssbruno cmd.enable = 1; 1548303628Ssbruno cmd.sta_id = sta_id; 1549303628Ssbruno cmd.tx_fifo = fifo; 1550303628Ssbruno cmd.aggregate = 0; 1551303628Ssbruno cmd.window = IWM_FRAME_LIMIT; 1552303628Ssbruno 1553303628Ssbruno error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, 1554303628Ssbruno sizeof(cmd), &cmd); 1555303628Ssbruno if (error) { 1556303628Ssbruno device_printf(sc->sc_dev, 1557303628Ssbruno "cannot enable txq %d\n", qid); 1558303628Ssbruno return error; 1559303628Ssbruno } 1560303628Ssbruno 1561303628Ssbruno if (!iwm_nic_lock(sc)) 1562303628Ssbruno return EBUSY; 1563303628Ssbruno } 1564303628Ssbruno 1565303628Ssbruno iwm_write_prph(sc, IWM_SCD_EN_CTRL, 1566303628Ssbruno iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); 1567303628Ssbruno 1568286441Srpaulo iwm_nic_unlock(sc); 1569286441Srpaulo 1570303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n", 1571286441Srpaulo __func__, qid, fifo); 1572303628Ssbruno 1573303628Ssbruno return 0; 1574286441Srpaulo} 1575286441Srpaulo 1576286441Srpaulostatic int 1577286441Srpauloiwm_post_alive(struct iwm_softc *sc) 1578286441Srpaulo{ 1579286441Srpaulo int nwords; 1580286441Srpaulo int error, chnl; 1581303628Ssbruno uint32_t base; 1582286441Srpaulo 1583286441Srpaulo if (!iwm_nic_lock(sc)) 1584286441Srpaulo return EBUSY; 1585286441Srpaulo 1586303628Ssbruno base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); 1587303628Ssbruno if (sc->sched_base != base) { 1588286441Srpaulo device_printf(sc->sc_dev, 1589303628Ssbruno "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", 1590303628Ssbruno __func__, sc->sched_base, base); 1591286441Srpaulo } 1592286441Srpaulo 1593286441Srpaulo iwm_ict_reset(sc); 1594286441Srpaulo 1595286441Srpaulo /* Clear TX scheduler state in SRAM. */ 1596286441Srpaulo nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - 1597286441Srpaulo IWM_SCD_CONTEXT_MEM_LOWER_BOUND) 1598286441Srpaulo / sizeof(uint32_t); 1599286441Srpaulo error = iwm_write_mem(sc, 1600286441Srpaulo sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, 1601286441Srpaulo NULL, nwords); 1602286441Srpaulo if (error) 1603286441Srpaulo goto out; 1604286441Srpaulo 1605286441Srpaulo /* Set physical address of TX scheduler rings (1KB aligned). */ 1606286441Srpaulo iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); 1607286441Srpaulo 1608286441Srpaulo iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); 1609286441Srpaulo 1610303628Ssbruno iwm_nic_unlock(sc); 1611303628Ssbruno 1612286441Srpaulo /* enable command channel */ 1613303628Ssbruno error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); 1614303628Ssbruno if (error) 1615303628Ssbruno return error; 1616286441Srpaulo 1617303628Ssbruno if (!iwm_nic_lock(sc)) 1618303628Ssbruno return EBUSY; 1619303628Ssbruno 1620286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); 1621286441Srpaulo 1622286441Srpaulo /* Enable DMA channels. */ 1623286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1624286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 1625286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 1626286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); 1627286441Srpaulo } 1628286441Srpaulo 1629286441Srpaulo IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, 1630286441Srpaulo IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); 1631286441Srpaulo 1632286441Srpaulo /* Enable L1-Active */ 1633303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { 1634303628Ssbruno iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, 1635303628Ssbruno IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 1636303628Ssbruno } 1637286441Srpaulo 1638286441Srpaulo out: 1639303628Ssbruno iwm_nic_unlock(sc); 1640286441Srpaulo return error; 1641286441Srpaulo} 1642286441Srpaulo 1643286441Srpaulo/* 1644286441Srpaulo * NVM read access and content parsing. We do not support 1645286441Srpaulo * external NVM or writing NVM. 1646286441Srpaulo * iwlwifi/mvm/nvm.c 1647286441Srpaulo */ 1648286441Srpaulo 1649286441Srpaulo/* list of NVM sections we are allowed/need to read */ 1650286441Srpauloconst int nvm_to_read[] = { 1651286441Srpaulo IWM_NVM_SECTION_TYPE_HW, 1652286441Srpaulo IWM_NVM_SECTION_TYPE_SW, 1653303628Ssbruno IWM_NVM_SECTION_TYPE_REGULATORY, 1654286441Srpaulo IWM_NVM_SECTION_TYPE_CALIBRATION, 1655286441Srpaulo IWM_NVM_SECTION_TYPE_PRODUCTION, 1656303628Ssbruno IWM_NVM_SECTION_TYPE_HW_8000, 1657303628Ssbruno IWM_NVM_SECTION_TYPE_MAC_OVERRIDE, 1658303628Ssbruno IWM_NVM_SECTION_TYPE_PHY_SKU, 1659286441Srpaulo}; 1660286441Srpaulo 1661286441Srpaulo/* Default NVM size to read */ 1662303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) 1663303628Ssbruno#define IWM_MAX_NVM_SECTION_SIZE 8192 1664286441Srpaulo 1665286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1 1666286441Srpaulo#define IWM_NVM_READ_OPCODE 0 1667286441Srpaulo 1668303628Ssbruno/* load nvm chunk response */ 1669303628Ssbruno#define IWM_READ_NVM_CHUNK_SUCCEED 0 1670303628Ssbruno#define IWM_READ_NVM_CHUNK_INVALID_ADDRESS 1 1671303628Ssbruno 1672286441Srpaulostatic int 1673286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, 1674286441Srpaulo uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) 1675286441Srpaulo{ 1676286441Srpaulo offset = 0; 1677286441Srpaulo struct iwm_nvm_access_cmd nvm_access_cmd = { 1678286441Srpaulo .offset = htole16(offset), 1679286441Srpaulo .length = htole16(length), 1680286441Srpaulo .type = htole16(section), 1681286441Srpaulo .op_code = IWM_NVM_READ_OPCODE, 1682286441Srpaulo }; 1683286441Srpaulo struct iwm_nvm_access_resp *nvm_resp; 1684286441Srpaulo struct iwm_rx_packet *pkt; 1685286441Srpaulo struct iwm_host_cmd cmd = { 1686286441Srpaulo .id = IWM_NVM_ACCESS_CMD, 1687286441Srpaulo .flags = IWM_CMD_SYNC | IWM_CMD_WANT_SKB | 1688286441Srpaulo IWM_CMD_SEND_IN_RFKILL, 1689286441Srpaulo .data = { &nvm_access_cmd, }, 1690286441Srpaulo }; 1691303628Ssbruno int ret, offset_read; 1692303628Ssbruno size_t bytes_read; 1693286441Srpaulo uint8_t *resp_data; 1694286441Srpaulo 1695286441Srpaulo cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); 1696286441Srpaulo 1697286441Srpaulo ret = iwm_send_cmd(sc, &cmd); 1698303628Ssbruno if (ret) { 1699303628Ssbruno device_printf(sc->sc_dev, 1700303628Ssbruno "Could not send NVM_ACCESS command (error=%d)\n", ret); 1701286441Srpaulo return ret; 1702303628Ssbruno } 1703286441Srpaulo 1704286441Srpaulo pkt = cmd.resp_pkt; 1705286441Srpaulo if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { 1706286441Srpaulo device_printf(sc->sc_dev, 1707303628Ssbruno "Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", 1708303628Ssbruno pkt->hdr.flags); 1709286441Srpaulo ret = EIO; 1710286441Srpaulo goto exit; 1711286441Srpaulo } 1712286441Srpaulo 1713286441Srpaulo /* Extract NVM response */ 1714286441Srpaulo nvm_resp = (void *)pkt->data; 1715286441Srpaulo 1716286441Srpaulo ret = le16toh(nvm_resp->status); 1717286441Srpaulo bytes_read = le16toh(nvm_resp->length); 1718286441Srpaulo offset_read = le16toh(nvm_resp->offset); 1719286441Srpaulo resp_data = nvm_resp->data; 1720286441Srpaulo if (ret) { 1721303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1722303628Ssbruno "NVM access command failed with status %d\n", ret); 1723286441Srpaulo ret = EINVAL; 1724286441Srpaulo goto exit; 1725286441Srpaulo } 1726286441Srpaulo 1727286441Srpaulo if (offset_read != offset) { 1728286441Srpaulo device_printf(sc->sc_dev, 1729303628Ssbruno "NVM ACCESS response with invalid offset %d\n", 1730303628Ssbruno offset_read); 1731286441Srpaulo ret = EINVAL; 1732286441Srpaulo goto exit; 1733286441Srpaulo } 1734286441Srpaulo 1735303628Ssbruno if (bytes_read > length) { 1736303628Ssbruno device_printf(sc->sc_dev, 1737303628Ssbruno "NVM ACCESS response with too much data " 1738303628Ssbruno "(%d bytes requested, %zd bytes received)\n", 1739303628Ssbruno length, bytes_read); 1740303628Ssbruno ret = EINVAL; 1741303628Ssbruno goto exit; 1742303628Ssbruno } 1743303628Ssbruno 1744286441Srpaulo memcpy(data + offset, resp_data, bytes_read); 1745286441Srpaulo *len = bytes_read; 1746286441Srpaulo 1747286441Srpaulo exit: 1748286441Srpaulo iwm_free_resp(sc, &cmd); 1749286441Srpaulo return ret; 1750286441Srpaulo} 1751286441Srpaulo 1752286441Srpaulo/* 1753286441Srpaulo * Reads an NVM section completely. 1754303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read 1755286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited 1756286441Srpaulo * by uCode, we need to manually check in this case that we don't 1757286441Srpaulo * overflow and try to read more than the EEPROM size. 1758286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and 1759286441Srpaulo * the uCode fills the response with as much data as we can, 1760286441Srpaulo * without overflowing, so no check is needed. 1761286441Srpaulo */ 1762286441Srpaulostatic int 1763286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc, 1764303628Ssbruno uint16_t section, uint8_t *data, uint16_t *len, size_t max_len) 1765286441Srpaulo{ 1766303628Ssbruno uint16_t chunklen, seglen; 1767303628Ssbruno int error = 0; 1768286441Srpaulo 1769303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1770303628Ssbruno "reading NVM section %d\n", section); 1771303628Ssbruno 1772303628Ssbruno chunklen = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; 1773286441Srpaulo *len = 0; 1774286441Srpaulo 1775303628Ssbruno /* Read NVM chunks until exhausted (reading less than requested) */ 1776303628Ssbruno while (seglen == chunklen && *len < max_len) { 1777286441Srpaulo error = iwm_nvm_read_chunk(sc, 1778303628Ssbruno section, *len, chunklen, data, &seglen); 1779286441Srpaulo if (error) { 1780303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1781303628Ssbruno "Cannot read from NVM section " 1782303628Ssbruno "%d at offset %d\n", section, *len); 1783286441Srpaulo return error; 1784286441Srpaulo } 1785286441Srpaulo *len += seglen; 1786286441Srpaulo } 1787286441Srpaulo 1788286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1789303628Ssbruno "NVM section %d read completed (%d bytes, error=%d)\n", 1790303628Ssbruno section, *len, error); 1791303628Ssbruno return error; 1792286441Srpaulo} 1793286441Srpaulo 1794286441Srpaulo/* 1795286441Srpaulo * BEGIN IWM_NVM_PARSE 1796286441Srpaulo */ 1797286441Srpaulo 1798286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */ 1799286441Srpaulo 1800286441Srpaulo/* NVM offsets (in words) definitions */ 1801303628Ssbrunoenum iwm_nvm_offsets { 1802286441Srpaulo /* NVM HW-Section offset (in words) definitions */ 1803286441Srpaulo IWM_HW_ADDR = 0x15, 1804286441Srpaulo 1805286441Srpaulo/* NVM SW-Section offset (in words) definitions */ 1806286441Srpaulo IWM_NVM_SW_SECTION = 0x1C0, 1807286441Srpaulo IWM_NVM_VERSION = 0, 1808286441Srpaulo IWM_RADIO_CFG = 1, 1809286441Srpaulo IWM_SKU = 2, 1810286441Srpaulo IWM_N_HW_ADDRS = 3, 1811286441Srpaulo IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION, 1812286441Srpaulo 1813286441Srpaulo/* NVM calibration section offset (in words) definitions */ 1814286441Srpaulo IWM_NVM_CALIB_SECTION = 0x2B8, 1815286441Srpaulo IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION 1816286441Srpaulo}; 1817286441Srpaulo 1818303628Ssbrunoenum iwm_8000_nvm_offsets { 1819303628Ssbruno /* NVM HW-Section offset (in words) definitions */ 1820303628Ssbruno IWM_HW_ADDR0_WFPM_8000 = 0x12, 1821303628Ssbruno IWM_HW_ADDR1_WFPM_8000 = 0x16, 1822303628Ssbruno IWM_HW_ADDR0_PCIE_8000 = 0x8A, 1823303628Ssbruno IWM_HW_ADDR1_PCIE_8000 = 0x8E, 1824303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000 = 1, 1825303628Ssbruno 1826303628Ssbruno /* NVM SW-Section offset (in words) definitions */ 1827303628Ssbruno IWM_NVM_SW_SECTION_8000 = 0x1C0, 1828303628Ssbruno IWM_NVM_VERSION_8000 = 0, 1829303628Ssbruno IWM_RADIO_CFG_8000 = 0, 1830303628Ssbruno IWM_SKU_8000 = 2, 1831303628Ssbruno IWM_N_HW_ADDRS_8000 = 3, 1832303628Ssbruno 1833303628Ssbruno /* NVM REGULATORY -Section offset (in words) definitions */ 1834303628Ssbruno IWM_NVM_CHANNELS_8000 = 0, 1835303628Ssbruno IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7, 1836303628Ssbruno IWM_NVM_LAR_OFFSET_8000 = 0x507, 1837303628Ssbruno IWM_NVM_LAR_ENABLED_8000 = 0x7, 1838303628Ssbruno 1839303628Ssbruno /* NVM calibration section offset (in words) definitions */ 1840303628Ssbruno IWM_NVM_CALIB_SECTION_8000 = 0x2B8, 1841303628Ssbruno IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000 1842303628Ssbruno}; 1843303628Ssbruno 1844286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */ 1845286441Srpauloenum nvm_sku_bits { 1846286441Srpaulo IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), 1847286441Srpaulo IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), 1848286441Srpaulo IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), 1849286441Srpaulo IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), 1850286441Srpaulo}; 1851286441Srpaulo 1852286441Srpaulo/* radio config bits (actual values from NVM definition) */ 1853286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ 1854286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ 1855286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ 1856286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ 1857286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ 1858286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ 1859286441Srpaulo 1860303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x) (x & 0xF) 1861303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x) ((x >> 4) & 0xF) 1862303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x) ((x >> 8) & 0xF) 1863303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x) ((x >> 12) & 0xFFF) 1864303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) 1865303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) 1866303628Ssbruno 1867286441Srpaulo#define DEFAULT_MAX_TX_POWER 16 1868286441Srpaulo 1869286441Srpaulo/** 1870286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM 1871286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo 1872286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel 1873286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed 1874286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required 1875298877Savos * XXX cannot find this (DFS) flag in iwl-nvm-parse.c 1876286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate 1877286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?) 1878286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) 1879286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) 1880286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) 1881286441Srpaulo */ 1882286441Srpauloenum iwm_nvm_channel_flags { 1883286441Srpaulo IWM_NVM_CHANNEL_VALID = (1 << 0), 1884286441Srpaulo IWM_NVM_CHANNEL_IBSS = (1 << 1), 1885286441Srpaulo IWM_NVM_CHANNEL_ACTIVE = (1 << 3), 1886286441Srpaulo IWM_NVM_CHANNEL_RADAR = (1 << 4), 1887286441Srpaulo IWM_NVM_CHANNEL_DFS = (1 << 7), 1888286441Srpaulo IWM_NVM_CHANNEL_WIDE = (1 << 8), 1889286441Srpaulo IWM_NVM_CHANNEL_40MHZ = (1 << 9), 1890286441Srpaulo IWM_NVM_CHANNEL_80MHZ = (1 << 10), 1891286441Srpaulo IWM_NVM_CHANNEL_160MHZ = (1 << 11), 1892286441Srpaulo}; 1893286441Srpaulo 1894286441Srpaulo/* 1895298877Savos * Translate EEPROM flags to net80211. 1896286441Srpaulo */ 1897298877Savosstatic uint32_t 1898298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags) 1899286441Srpaulo{ 1900298877Savos uint32_t nflags; 1901286441Srpaulo 1902298877Savos nflags = 0; 1903298877Savos if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0) 1904298877Savos nflags |= IEEE80211_CHAN_PASSIVE; 1905298877Savos if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0) 1906298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1907298877Savos if (ch_flags & IWM_NVM_CHANNEL_RADAR) { 1908298877Savos nflags |= IEEE80211_CHAN_DFS; 1909298877Savos /* Just in case. */ 1910298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1911286441Srpaulo } 1912286441Srpaulo 1913298877Savos return (nflags); 1914286441Srpaulo} 1915286441Srpaulo 1916286441Srpaulostatic void 1917298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[], 1918303628Ssbruno int maxchans, int *nchans, int ch_idx, size_t ch_num, 1919303628Ssbruno const uint8_t bands[]) 1920286441Srpaulo{ 1921298877Savos const uint16_t * const nvm_ch_flags = sc->sc_nvm.nvm_ch_flags; 1922298877Savos uint32_t nflags; 1923286441Srpaulo uint16_t ch_flags; 1924298877Savos uint8_t ieee; 1925298877Savos int error; 1926286441Srpaulo 1927298877Savos for (; ch_idx < ch_num; ch_idx++) { 1928286441Srpaulo ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); 1929303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) 1930303628Ssbruno ieee = iwm_nvm_channels[ch_idx]; 1931303628Ssbruno else 1932303628Ssbruno ieee = iwm_nvm_channels_8000[ch_idx]; 1933286441Srpaulo 1934286441Srpaulo if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { 1935286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1936286441Srpaulo "Ch. %d Flags %x [%sGHz] - No traffic\n", 1937298877Savos ieee, ch_flags, 1938286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1939286441Srpaulo "5.2" : "2.4"); 1940286441Srpaulo continue; 1941286441Srpaulo } 1942286441Srpaulo 1943298877Savos nflags = iwm_eeprom_channel_flags(ch_flags); 1944298877Savos error = ieee80211_add_channel(chans, maxchans, nchans, 1945298877Savos ieee, 0, 0, nflags, bands); 1946298877Savos if (error != 0) 1947298877Savos break; 1948286441Srpaulo 1949286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1950286441Srpaulo "Ch. %d Flags %x [%sGHz] - Added\n", 1951298877Savos ieee, ch_flags, 1952286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1953286441Srpaulo "5.2" : "2.4"); 1954286441Srpaulo } 1955286441Srpaulo} 1956286441Srpaulo 1957298877Savosstatic void 1958298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans, 1959298877Savos struct ieee80211_channel chans[]) 1960298877Savos{ 1961298877Savos struct iwm_softc *sc = ic->ic_softc; 1962298877Savos struct iwm_nvm_data *data = &sc->sc_nvm; 1963299883Skevlo uint8_t bands[IEEE80211_MODE_BYTES]; 1964303628Ssbruno size_t ch_num; 1965298877Savos 1966298877Savos memset(bands, 0, sizeof(bands)); 1967298877Savos /* 1-13: 11b/g channels. */ 1968298877Savos setbit(bands, IEEE80211_MODE_11B); 1969298877Savos setbit(bands, IEEE80211_MODE_11G); 1970298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 0, 1971298877Savos IWM_NUM_2GHZ_CHANNELS - 1, bands); 1972298877Savos 1973298877Savos /* 14: 11b channel only. */ 1974298877Savos clrbit(bands, IEEE80211_MODE_11G); 1975298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 1976298936Savos IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands); 1977298877Savos 1978298877Savos if (data->sku_cap_band_52GHz_enable) { 1979303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) 1980303628Ssbruno ch_num = nitems(iwm_nvm_channels); 1981303628Ssbruno else 1982303628Ssbruno ch_num = nitems(iwm_nvm_channels_8000); 1983298877Savos memset(bands, 0, sizeof(bands)); 1984298877Savos setbit(bands, IEEE80211_MODE_11A); 1985298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 1986303628Ssbruno IWM_NUM_2GHZ_CHANNELS, ch_num, bands); 1987298877Savos } 1988298877Savos} 1989298877Savos 1990303628Ssbrunostatic void 1991303628Ssbrunoiwm_set_hw_address_8000(struct iwm_softc *sc, struct iwm_nvm_data *data, 1992303628Ssbruno const uint16_t *mac_override, const uint16_t *nvm_hw) 1993303628Ssbruno{ 1994303628Ssbruno const uint8_t *hw_addr; 1995303628Ssbruno 1996303628Ssbruno if (mac_override) { 1997303628Ssbruno static const uint8_t reserved_mac[] = { 1998303628Ssbruno 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 1999303628Ssbruno }; 2000303628Ssbruno 2001303628Ssbruno hw_addr = (const uint8_t *)(mac_override + 2002303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000); 2003303628Ssbruno 2004303628Ssbruno /* 2005303628Ssbruno * Store the MAC address from MAO section. 2006303628Ssbruno * No byte swapping is required in MAO section 2007303628Ssbruno */ 2008303628Ssbruno IEEE80211_ADDR_COPY(data->hw_addr, hw_addr); 2009303628Ssbruno 2010303628Ssbruno /* 2011303628Ssbruno * Force the use of the OTP MAC address in case of reserved MAC 2012303628Ssbruno * address in the NVM, or if address is given but invalid. 2013303628Ssbruno */ 2014303628Ssbruno if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) && 2015303628Ssbruno !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) && 2016303628Ssbruno iwm_is_valid_ether_addr(data->hw_addr) && 2017303628Ssbruno !IEEE80211_IS_MULTICAST(data->hw_addr)) 2018303628Ssbruno return; 2019303628Ssbruno 2020303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2021303628Ssbruno "%s: mac address from nvm override section invalid\n", 2022303628Ssbruno __func__); 2023303628Ssbruno } 2024303628Ssbruno 2025303628Ssbruno if (nvm_hw) { 2026303628Ssbruno /* read the mac address from WFMP registers */ 2027303628Ssbruno uint32_t mac_addr0 = 2028303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0)); 2029303628Ssbruno uint32_t mac_addr1 = 2030303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1)); 2031303628Ssbruno 2032303628Ssbruno hw_addr = (const uint8_t *)&mac_addr0; 2033303628Ssbruno data->hw_addr[0] = hw_addr[3]; 2034303628Ssbruno data->hw_addr[1] = hw_addr[2]; 2035303628Ssbruno data->hw_addr[2] = hw_addr[1]; 2036303628Ssbruno data->hw_addr[3] = hw_addr[0]; 2037303628Ssbruno 2038303628Ssbruno hw_addr = (const uint8_t *)&mac_addr1; 2039303628Ssbruno data->hw_addr[4] = hw_addr[1]; 2040303628Ssbruno data->hw_addr[5] = hw_addr[0]; 2041303628Ssbruno 2042303628Ssbruno return; 2043303628Ssbruno } 2044303628Ssbruno 2045303628Ssbruno device_printf(sc->sc_dev, "%s: mac address not found\n", __func__); 2046303628Ssbruno memset(data->hw_addr, 0, sizeof(data->hw_addr)); 2047303628Ssbruno} 2048303628Ssbruno 2049286441Srpaulostatic int 2050303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2051303628Ssbruno const uint16_t *phy_sku) 2052303628Ssbruno{ 2053303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) 2054303628Ssbruno return le16_to_cpup(nvm_sw + IWM_SKU); 2055303628Ssbruno 2056303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000)); 2057303628Ssbruno} 2058303628Ssbruno 2059303628Ssbrunostatic int 2060303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2061303628Ssbruno{ 2062303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) 2063303628Ssbruno return le16_to_cpup(nvm_sw + IWM_NVM_VERSION); 2064303628Ssbruno else 2065303628Ssbruno return le32_to_cpup((const uint32_t *)(nvm_sw + 2066303628Ssbruno IWM_NVM_VERSION_8000)); 2067303628Ssbruno} 2068303628Ssbruno 2069303628Ssbrunostatic int 2070303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2071303628Ssbruno const uint16_t *phy_sku) 2072303628Ssbruno{ 2073303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) 2074303628Ssbruno return le16_to_cpup(nvm_sw + IWM_RADIO_CFG); 2075303628Ssbruno 2076303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); 2077303628Ssbruno} 2078303628Ssbruno 2079303628Ssbrunostatic int 2080303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2081303628Ssbruno{ 2082303628Ssbruno int n_hw_addr; 2083303628Ssbruno 2084303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) 2085303628Ssbruno return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); 2086303628Ssbruno 2087303628Ssbruno n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000)); 2088303628Ssbruno 2089303628Ssbruno return n_hw_addr & IWM_N_HW_ADDR_MASK; 2090303628Ssbruno} 2091303628Ssbruno 2092303628Ssbrunostatic void 2093303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data, 2094303628Ssbruno uint32_t radio_cfg) 2095303628Ssbruno{ 2096303628Ssbruno if (sc->sc_device_family != IWM_DEVICE_FAMILY_8000) { 2097303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); 2098303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); 2099303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); 2100303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); 2101303628Ssbruno return; 2102303628Ssbruno } 2103303628Ssbruno 2104303628Ssbruno /* set the radio configuration for family 8000 */ 2105303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg); 2106303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg); 2107303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg); 2108303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg); 2109303628Ssbruno data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg); 2110303628Ssbruno data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg); 2111303628Ssbruno} 2112303628Ssbruno 2113303628Ssbrunostatic int 2114286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc, 2115303628Ssbruno const uint16_t *nvm_hw, const uint16_t *nvm_sw, 2116303628Ssbruno const uint16_t *nvm_calib, const uint16_t *mac_override, 2117303628Ssbruno const uint16_t *phy_sku, const uint16_t *regulatory) 2118286441Srpaulo{ 2119286441Srpaulo struct iwm_nvm_data *data = &sc->sc_nvm; 2120286441Srpaulo uint8_t hw_addr[IEEE80211_ADDR_LEN]; 2121303628Ssbruno uint32_t sku, radio_cfg; 2122286441Srpaulo 2123303628Ssbruno data->nvm_version = iwm_get_nvm_version(sc, nvm_sw); 2124286441Srpaulo 2125303628Ssbruno radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku); 2126303628Ssbruno iwm_set_radio_cfg(sc, data, radio_cfg); 2127286441Srpaulo 2128303628Ssbruno sku = iwm_get_sku(sc, nvm_sw, phy_sku); 2129286441Srpaulo data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; 2130286441Srpaulo data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; 2131286441Srpaulo data->sku_cap_11n_enable = 0; 2132286441Srpaulo 2133303628Ssbruno data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw); 2134286441Srpaulo 2135286441Srpaulo /* The byte order is little endian 16 bit, meaning 214365 */ 2136303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { 2137303628Ssbruno IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR); 2138303628Ssbruno data->hw_addr[0] = hw_addr[1]; 2139303628Ssbruno data->hw_addr[1] = hw_addr[0]; 2140303628Ssbruno data->hw_addr[2] = hw_addr[3]; 2141303628Ssbruno data->hw_addr[3] = hw_addr[2]; 2142303628Ssbruno data->hw_addr[4] = hw_addr[5]; 2143303628Ssbruno data->hw_addr[5] = hw_addr[4]; 2144303628Ssbruno } else { 2145303628Ssbruno iwm_set_hw_address_8000(sc, data, mac_override, nvm_hw); 2146303628Ssbruno } 2147286441Srpaulo 2148303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { 2149303628Ssbruno memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS], 2150303628Ssbruno IWM_NUM_CHANNELS * sizeof(uint16_t)); 2151303628Ssbruno } else { 2152303628Ssbruno memcpy(data->nvm_ch_flags, ®ulatory[IWM_NVM_CHANNELS_8000], 2153303628Ssbruno IWM_NUM_CHANNELS_8000 * sizeof(uint16_t)); 2154303628Ssbruno } 2155286441Srpaulo 2156286441Srpaulo return 0; 2157286441Srpaulo} 2158286441Srpaulo 2159286441Srpaulo/* 2160286441Srpaulo * END NVM PARSE 2161286441Srpaulo */ 2162286441Srpaulo 2163286441Srpaulostatic int 2164286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) 2165286441Srpaulo{ 2166303628Ssbruno const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; 2167286441Srpaulo 2168286441Srpaulo /* Checking for required sections */ 2169303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) { 2170303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2171303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_HW].data) { 2172303628Ssbruno device_printf(sc->sc_dev, 2173303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2174303628Ssbruno return ENOENT; 2175303628Ssbruno } 2176303628Ssbruno 2177303628Ssbruno hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data; 2178303628Ssbruno } else if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { 2179303628Ssbruno /* SW and REGULATORY sections are mandatory */ 2180303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2181303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) { 2182303628Ssbruno device_printf(sc->sc_dev, 2183303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2184303628Ssbruno return ENOENT; 2185303628Ssbruno } 2186303628Ssbruno /* MAC_OVERRIDE or at least HW section must exist */ 2187303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_HW_8000].data && 2188303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) { 2189303628Ssbruno device_printf(sc->sc_dev, 2190303628Ssbruno "Can't parse mac_address, empty sections\n"); 2191303628Ssbruno return ENOENT; 2192303628Ssbruno } 2193303628Ssbruno 2194303628Ssbruno /* PHY_SKU section is mandatory in B0 */ 2195303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) { 2196303628Ssbruno device_printf(sc->sc_dev, 2197303628Ssbruno "Can't parse phy_sku in B0, empty sections\n"); 2198303628Ssbruno return ENOENT; 2199303628Ssbruno } 2200303628Ssbruno 2201303628Ssbruno hw = (const uint16_t *) 2202303628Ssbruno sections[IWM_NVM_SECTION_TYPE_HW_8000].data; 2203303628Ssbruno } else { 2204303628Ssbruno panic("unknown device family %d\n", sc->sc_device_family); 2205286441Srpaulo } 2206286441Srpaulo 2207286441Srpaulo sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; 2208303628Ssbruno calib = (const uint16_t *) 2209303628Ssbruno sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; 2210303628Ssbruno regulatory = (const uint16_t *) 2211303628Ssbruno sections[IWM_NVM_SECTION_TYPE_REGULATORY].data; 2212303628Ssbruno mac_override = (const uint16_t *) 2213303628Ssbruno sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data; 2214303628Ssbruno phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data; 2215303628Ssbruno 2216303628Ssbruno return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override, 2217303628Ssbruno phy_sku, regulatory); 2218286441Srpaulo} 2219286441Srpaulo 2220286441Srpaulostatic int 2221286441Srpauloiwm_nvm_init(struct iwm_softc *sc) 2222286441Srpaulo{ 2223286441Srpaulo struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS]; 2224286441Srpaulo int i, section, error; 2225286441Srpaulo uint16_t len; 2226303628Ssbruno uint8_t *buf; 2227303628Ssbruno const size_t bufsz = IWM_MAX_NVM_SECTION_SIZE; 2228286441Srpaulo 2229303628Ssbruno memset(nvm_sections, 0 , sizeof(nvm_sections)); 2230286441Srpaulo 2231303628Ssbruno buf = malloc(bufsz, M_DEVBUF, M_NOWAIT); 2232303628Ssbruno if (buf == NULL) 2233303628Ssbruno return ENOMEM; 2234301970Sadrian 2235286441Srpaulo for (i = 0; i < nitems(nvm_to_read); i++) { 2236286441Srpaulo section = nvm_to_read[i]; 2237286441Srpaulo KASSERT(section <= nitems(nvm_sections), 2238286441Srpaulo ("too many sections")); 2239286441Srpaulo 2240303628Ssbruno error = iwm_nvm_read_section(sc, section, buf, &len, bufsz); 2241303628Ssbruno if (error) { 2242303628Ssbruno error = 0; 2243303628Ssbruno continue; 2244303628Ssbruno } 2245303628Ssbruno nvm_sections[section].data = malloc(len, M_DEVBUF, M_NOWAIT); 2246303628Ssbruno if (nvm_sections[section].data == NULL) { 2247286441Srpaulo error = ENOMEM; 2248286441Srpaulo break; 2249286441Srpaulo } 2250303628Ssbruno memcpy(nvm_sections[section].data, buf, len); 2251286441Srpaulo nvm_sections[section].length = len; 2252286441Srpaulo } 2253303628Ssbruno free(buf, M_DEVBUF); 2254301970Sadrian if (error == 0) 2255301970Sadrian error = iwm_parse_nvm_sections(sc, nvm_sections); 2256286441Srpaulo 2257301970Sadrian for (i = 0; i < IWM_NVM_NUM_OF_SECTIONS; i++) { 2258301970Sadrian if (nvm_sections[i].data != NULL) 2259301970Sadrian free(nvm_sections[i].data, M_DEVBUF); 2260301970Sadrian } 2261301970Sadrian 2262301970Sadrian return error; 2263286441Srpaulo} 2264286441Srpaulo 2265286441Srpaulo/* 2266286441Srpaulo * Firmware loading gunk. This is kind of a weird hybrid between the 2267286441Srpaulo * iwn driver and the Linux iwlwifi driver. 2268286441Srpaulo */ 2269286441Srpaulo 2270286441Srpaulostatic int 2271303628Ssbrunoiwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr, 2272286441Srpaulo const uint8_t *section, uint32_t byte_cnt) 2273286441Srpaulo{ 2274303628Ssbruno int error = EINVAL; 2275303628Ssbruno uint32_t chunk_sz, offset; 2276303628Ssbruno 2277303628Ssbruno chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt); 2278303628Ssbruno 2279303628Ssbruno for (offset = 0; offset < byte_cnt; offset += chunk_sz) { 2280303628Ssbruno uint32_t addr, len; 2281303628Ssbruno const uint8_t *data; 2282303628Ssbruno 2283303628Ssbruno addr = dst_addr + offset; 2284303628Ssbruno len = MIN(chunk_sz, byte_cnt - offset); 2285303628Ssbruno data = section + offset; 2286303628Ssbruno 2287303628Ssbruno error = iwm_firmware_load_chunk(sc, addr, data, len); 2288303628Ssbruno if (error) 2289303628Ssbruno break; 2290303628Ssbruno } 2291303628Ssbruno 2292303628Ssbruno return error; 2293303628Ssbruno} 2294303628Ssbruno 2295303628Ssbrunostatic int 2296303628Ssbrunoiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, 2297303628Ssbruno const uint8_t *chunk, uint32_t byte_cnt) 2298303628Ssbruno{ 2299286441Srpaulo struct iwm_dma_info *dma = &sc->fw_dma; 2300286441Srpaulo int error; 2301286441Srpaulo 2302303628Ssbruno /* Copy firmware chunk into pre-allocated DMA-safe memory. */ 2303303628Ssbruno memcpy(dma->vaddr, chunk, byte_cnt); 2304286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 2305286441Srpaulo 2306303628Ssbruno if (dst_addr >= IWM_FW_MEM_EXTENDED_START && 2307303628Ssbruno dst_addr <= IWM_FW_MEM_EXTENDED_END) { 2308303628Ssbruno iwm_set_bits_prph(sc, IWM_LMPM_CHICK, 2309303628Ssbruno IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2310303628Ssbruno } 2311303628Ssbruno 2312303628Ssbruno sc->sc_fw_chunk_done = 0; 2313303628Ssbruno 2314286441Srpaulo if (!iwm_nic_lock(sc)) 2315286441Srpaulo return EBUSY; 2316286441Srpaulo 2317286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2318286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 2319286441Srpaulo IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), 2320286441Srpaulo dst_addr); 2321286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), 2322286441Srpaulo dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 2323286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), 2324286441Srpaulo (iwm_get_dma_hi_addr(dma->paddr) 2325286441Srpaulo << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 2326286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 2327286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 2328286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | 2329286441Srpaulo IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 2330286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2331286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 2332286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 2333286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 2334286441Srpaulo 2335286441Srpaulo iwm_nic_unlock(sc); 2336286441Srpaulo 2337286441Srpaulo /* wait 1s for this segment to load */ 2338286441Srpaulo while (!sc->sc_fw_chunk_done) 2339286441Srpaulo if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0) 2340286441Srpaulo break; 2341286441Srpaulo 2342303628Ssbruno if (!sc->sc_fw_chunk_done) { 2343303628Ssbruno device_printf(sc->sc_dev, 2344303628Ssbruno "fw chunk addr 0x%x len %d failed to load\n", 2345303628Ssbruno dst_addr, byte_cnt); 2346303628Ssbruno } 2347303628Ssbruno 2348303628Ssbruno if (dst_addr >= IWM_FW_MEM_EXTENDED_START && 2349303628Ssbruno dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) { 2350303628Ssbruno iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, 2351303628Ssbruno IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2352303628Ssbruno iwm_nic_unlock(sc); 2353303628Ssbruno } 2354303628Ssbruno 2355286441Srpaulo return error; 2356286441Srpaulo} 2357286441Srpaulo 2358303628Ssbrunoint 2359303628Ssbrunoiwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, 2360303628Ssbruno int cpu, int *first_ucode_section) 2361303628Ssbruno{ 2362303628Ssbruno int shift_param; 2363303628Ssbruno int i, error = 0, sec_num = 0x1; 2364303628Ssbruno uint32_t val, last_read_idx = 0; 2365303628Ssbruno const void *data; 2366303628Ssbruno uint32_t dlen; 2367303628Ssbruno uint32_t offset; 2368303628Ssbruno 2369303628Ssbruno if (cpu == 1) { 2370303628Ssbruno shift_param = 0; 2371303628Ssbruno *first_ucode_section = 0; 2372303628Ssbruno } else { 2373303628Ssbruno shift_param = 16; 2374303628Ssbruno (*first_ucode_section)++; 2375303628Ssbruno } 2376303628Ssbruno 2377303628Ssbruno for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) { 2378303628Ssbruno last_read_idx = i; 2379303628Ssbruno data = fws->fw_sect[i].fws_data; 2380303628Ssbruno dlen = fws->fw_sect[i].fws_len; 2381303628Ssbruno offset = fws->fw_sect[i].fws_devoff; 2382303628Ssbruno 2383303628Ssbruno /* 2384303628Ssbruno * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 2385303628Ssbruno * CPU1 to CPU2. 2386303628Ssbruno * PAGING_SEPARATOR_SECTION delimiter - separate between 2387303628Ssbruno * CPU2 non paged to CPU2 paging sec. 2388303628Ssbruno */ 2389303628Ssbruno if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || 2390303628Ssbruno offset == IWM_PAGING_SEPARATOR_SECTION) 2391303628Ssbruno break; 2392303628Ssbruno 2393303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2394303628Ssbruno "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n", 2395303628Ssbruno i, offset, dlen, cpu); 2396303628Ssbruno 2397303628Ssbruno if (dlen > sc->sc_fwdmasegsz) { 2398303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2399303628Ssbruno "chunk %d too large (%d bytes)\n", i, dlen); 2400303628Ssbruno error = EFBIG; 2401303628Ssbruno } else { 2402303628Ssbruno error = iwm_firmware_load_sect(sc, offset, data, dlen); 2403303628Ssbruno } 2404303628Ssbruno if (error) { 2405303628Ssbruno device_printf(sc->sc_dev, 2406303628Ssbruno "could not load firmware chunk %d (error %d)\n", 2407303628Ssbruno i, error); 2408303628Ssbruno return error; 2409303628Ssbruno } 2410303628Ssbruno 2411303628Ssbruno /* Notify the ucode of the loaded section number and status */ 2412303628Ssbruno if (iwm_nic_lock(sc)) { 2413303628Ssbruno val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS); 2414303628Ssbruno val = val | (sec_num << shift_param); 2415303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val); 2416303628Ssbruno sec_num = (sec_num << 1) | 0x1; 2417303628Ssbruno iwm_nic_unlock(sc); 2418303628Ssbruno 2419303628Ssbruno /* 2420303628Ssbruno * The firmware won't load correctly without this delay. 2421303628Ssbruno */ 2422303628Ssbruno DELAY(8000); 2423303628Ssbruno } 2424303628Ssbruno } 2425303628Ssbruno 2426303628Ssbruno *first_ucode_section = last_read_idx; 2427303628Ssbruno 2428303628Ssbruno if (iwm_nic_lock(sc)) { 2429303628Ssbruno if (cpu == 1) 2430303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); 2431303628Ssbruno else 2432303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); 2433303628Ssbruno iwm_nic_unlock(sc); 2434303628Ssbruno } 2435303628Ssbruno 2436303628Ssbruno return 0; 2437303628Ssbruno} 2438303628Ssbruno 2439303628Ssbrunoint 2440303628Ssbrunoiwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2441303628Ssbruno{ 2442303628Ssbruno struct iwm_fw_sects *fws; 2443303628Ssbruno int error = 0; 2444303628Ssbruno int first_ucode_section; 2445303628Ssbruno 2446303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n", 2447303628Ssbruno ucode_type); 2448303628Ssbruno 2449303628Ssbruno fws = &sc->sc_fw.fw_sects[ucode_type]; 2450303628Ssbruno 2451303628Ssbruno /* configure the ucode to be ready to get the secured image */ 2452303628Ssbruno /* release CPU reset */ 2453303628Ssbruno iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT); 2454303628Ssbruno 2455303628Ssbruno /* load to FW the binary Secured sections of CPU1 */ 2456303628Ssbruno error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section); 2457303628Ssbruno if (error) 2458303628Ssbruno return error; 2459303628Ssbruno 2460303628Ssbruno /* load to FW the binary sections of CPU2 */ 2461303628Ssbruno return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section); 2462303628Ssbruno} 2463303628Ssbruno 2464286441Srpaulostatic int 2465303628Ssbrunoiwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2466286441Srpaulo{ 2467286441Srpaulo struct iwm_fw_sects *fws; 2468303628Ssbruno int error, i; 2469286441Srpaulo const void *data; 2470286441Srpaulo uint32_t dlen; 2471286441Srpaulo uint32_t offset; 2472286441Srpaulo 2473286441Srpaulo sc->sc_uc.uc_intr = 0; 2474286441Srpaulo 2475286441Srpaulo fws = &sc->sc_fw.fw_sects[ucode_type]; 2476286441Srpaulo for (i = 0; i < fws->fw_count; i++) { 2477286441Srpaulo data = fws->fw_sect[i].fws_data; 2478286441Srpaulo dlen = fws->fw_sect[i].fws_len; 2479286441Srpaulo offset = fws->fw_sect[i].fws_devoff; 2480286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 2481286441Srpaulo "LOAD FIRMWARE type %d offset %u len %d\n", 2482286441Srpaulo ucode_type, offset, dlen); 2483303628Ssbruno if (dlen > sc->sc_fwdmasegsz) { 2484303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 2485303628Ssbruno "chunk %d too large (%d bytes)\n", i, dlen); 2486303628Ssbruno error = EFBIG; 2487303628Ssbruno } else { 2488303628Ssbruno error = iwm_firmware_load_sect(sc, offset, data, dlen); 2489303628Ssbruno } 2490286441Srpaulo if (error) { 2491286441Srpaulo device_printf(sc->sc_dev, 2492303628Ssbruno "could not load firmware chunk %u of %u " 2493303628Ssbruno "(error=%d)\n", i, fws->fw_count, error); 2494286441Srpaulo return error; 2495286441Srpaulo } 2496286441Srpaulo } 2497286441Srpaulo 2498286441Srpaulo IWM_WRITE(sc, IWM_CSR_RESET, 0); 2499286441Srpaulo 2500303628Ssbruno return 0; 2501303628Ssbruno} 2502303628Ssbruno 2503303628Ssbrunostatic int 2504303628Ssbrunoiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2505303628Ssbruno{ 2506303628Ssbruno int error, w; 2507303628Ssbruno 2508303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) 2509303628Ssbruno error = iwm_load_firmware_8000(sc, ucode_type); 2510303628Ssbruno else 2511303628Ssbruno error = iwm_load_firmware_7000(sc, ucode_type); 2512303628Ssbruno if (error) 2513303628Ssbruno return error; 2514303628Ssbruno 2515303628Ssbruno /* wait for the firmware to load */ 2516286441Srpaulo for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { 2517286441Srpaulo error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10); 2518286441Srpaulo } 2519303628Ssbruno if (error || !sc->sc_uc.uc_ok) { 2520303628Ssbruno device_printf(sc->sc_dev, "could not load firmware\n"); 2521303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { 2522303628Ssbruno device_printf(sc->sc_dev, "cpu1 status: 0x%x\n", 2523303628Ssbruno iwm_read_prph(sc, IWM_SB_CPU_1_STATUS)); 2524303628Ssbruno device_printf(sc->sc_dev, "cpu2 status: 0x%x\n", 2525303628Ssbruno iwm_read_prph(sc, IWM_SB_CPU_2_STATUS)); 2526303628Ssbruno } 2527303628Ssbruno } 2528286441Srpaulo 2529303628Ssbruno /* 2530303628Ssbruno * Give the firmware some time to initialize. 2531303628Ssbruno * Accessing it too early causes errors. 2532303628Ssbruno */ 2533303628Ssbruno msleep(&w, &sc->sc_mtx, 0, "iwmfwinit", hz); 2534303628Ssbruno 2535286441Srpaulo return error; 2536286441Srpaulo} 2537286441Srpaulo 2538286441Srpaulo/* iwlwifi: pcie/trans.c */ 2539286441Srpaulostatic int 2540286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2541286441Srpaulo{ 2542286441Srpaulo int error; 2543286441Srpaulo 2544286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 2545286441Srpaulo 2546286441Srpaulo if ((error = iwm_nic_init(sc)) != 0) { 2547286441Srpaulo device_printf(sc->sc_dev, "unable to init nic\n"); 2548286441Srpaulo return error; 2549286441Srpaulo } 2550286441Srpaulo 2551286441Srpaulo /* make sure rfkill handshake bits are cleared */ 2552286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2553286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, 2554286441Srpaulo IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 2555286441Srpaulo 2556286441Srpaulo /* clear (again), then enable host interrupts */ 2557286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 2558286441Srpaulo iwm_enable_interrupts(sc); 2559286441Srpaulo 2560286441Srpaulo /* really make sure rfkill handshake bits are cleared */ 2561286441Srpaulo /* maybe we should write a few times more? just to make sure */ 2562286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2563286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2564286441Srpaulo 2565286441Srpaulo /* Load the given image to the HW */ 2566286441Srpaulo return iwm_load_firmware(sc, ucode_type); 2567286441Srpaulo} 2568286441Srpaulo 2569286441Srpaulostatic int 2570286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) 2571286441Srpaulo{ 2572286441Srpaulo struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { 2573286441Srpaulo .valid = htole32(valid_tx_ant), 2574286441Srpaulo }; 2575286441Srpaulo 2576286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, 2577286441Srpaulo IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); 2578286441Srpaulo} 2579286441Srpaulo 2580286441Srpaulo/* iwlwifi: mvm/fw.c */ 2581286441Srpaulostatic int 2582286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc) 2583286441Srpaulo{ 2584286441Srpaulo struct iwm_phy_cfg_cmd phy_cfg_cmd; 2585286441Srpaulo enum iwm_ucode_type ucode_type = sc->sc_uc_current; 2586286441Srpaulo 2587286441Srpaulo /* Set parameters */ 2588286441Srpaulo phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); 2589286441Srpaulo phy_cfg_cmd.calib_control.event_trigger = 2590286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger; 2591286441Srpaulo phy_cfg_cmd.calib_control.flow_trigger = 2592286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger; 2593286441Srpaulo 2594286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 2595286441Srpaulo "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); 2596286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, 2597286441Srpaulo sizeof(phy_cfg_cmd), &phy_cfg_cmd); 2598286441Srpaulo} 2599286441Srpaulo 2600286441Srpaulostatic int 2601286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, 2602286441Srpaulo enum iwm_ucode_type ucode_type) 2603286441Srpaulo{ 2604286441Srpaulo enum iwm_ucode_type old_type = sc->sc_uc_current; 2605286441Srpaulo int error; 2606286441Srpaulo 2607303628Ssbruno if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { 2608303628Ssbruno device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", 2609303628Ssbruno error); 2610286441Srpaulo return error; 2611303628Ssbruno } 2612286441Srpaulo 2613286441Srpaulo sc->sc_uc_current = ucode_type; 2614286441Srpaulo error = iwm_start_fw(sc, ucode_type); 2615286441Srpaulo if (error) { 2616303628Ssbruno device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error); 2617286441Srpaulo sc->sc_uc_current = old_type; 2618286441Srpaulo return error; 2619286441Srpaulo } 2620286441Srpaulo 2621303628Ssbruno error = iwm_post_alive(sc); 2622303628Ssbruno if (error) { 2623303628Ssbruno device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error); 2624303628Ssbruno } 2625303628Ssbruno return error; 2626286441Srpaulo} 2627286441Srpaulo 2628286441Srpaulo/* 2629286441Srpaulo * mvm misc bits 2630286441Srpaulo */ 2631286441Srpaulo 2632286441Srpaulo/* 2633286441Srpaulo * follows iwlwifi/fw.c 2634286441Srpaulo */ 2635286441Srpaulostatic int 2636286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) 2637286441Srpaulo{ 2638286441Srpaulo int error; 2639286441Srpaulo 2640286441Srpaulo /* do not operate with rfkill switch turned on */ 2641286441Srpaulo if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { 2642286441Srpaulo device_printf(sc->sc_dev, 2643286441Srpaulo "radio is disabled by hardware switch\n"); 2644286441Srpaulo return EPERM; 2645286441Srpaulo } 2646286441Srpaulo 2647286441Srpaulo sc->sc_init_complete = 0; 2648286441Srpaulo if ((error = iwm_mvm_load_ucode_wait_alive(sc, 2649301192Sadrian IWM_UCODE_TYPE_INIT)) != 0) { 2650301192Sadrian device_printf(sc->sc_dev, "failed to load init firmware\n"); 2651286441Srpaulo return error; 2652301192Sadrian } 2653286441Srpaulo 2654286441Srpaulo if (justnvm) { 2655286441Srpaulo if ((error = iwm_nvm_init(sc)) != 0) { 2656286441Srpaulo device_printf(sc->sc_dev, "failed to read nvm\n"); 2657286441Srpaulo return error; 2658286441Srpaulo } 2659289729Skevlo IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->sc_nvm.hw_addr); 2660286441Srpaulo 2661286441Srpaulo return 0; 2662286441Srpaulo } 2663286441Srpaulo 2664303628Ssbruno if ((error = iwm_send_bt_init_conf(sc)) != 0) { 2665303628Ssbruno device_printf(sc->sc_dev, 2666303628Ssbruno "failed to send bt coex configuration: %d\n", error); 2667303628Ssbruno return error; 2668303628Ssbruno } 2669303628Ssbruno 2670303628Ssbruno /* Init Smart FIFO. */ 2671303628Ssbruno error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF); 2672303628Ssbruno if (error != 0) 2673303628Ssbruno return error; 2674303628Ssbruno 2675286441Srpaulo /* Send TX valid antennas before triggering calibrations */ 2676303628Ssbruno if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { 2677303628Ssbruno device_printf(sc->sc_dev, 2678303628Ssbruno "failed to send antennas before calibration: %d\n", error); 2679286441Srpaulo return error; 2680303628Ssbruno } 2681286441Srpaulo 2682286441Srpaulo /* 2683303628Ssbruno * Send phy configurations command to init uCode 2684303628Ssbruno * to start the 16.0 uCode init image internal calibrations. 2685303628Ssbruno */ 2686286441Srpaulo if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { 2687286441Srpaulo device_printf(sc->sc_dev, 2688286441Srpaulo "%s: failed to run internal calibration: %d\n", 2689286441Srpaulo __func__, error); 2690286441Srpaulo return error; 2691286441Srpaulo } 2692286441Srpaulo 2693286441Srpaulo /* 2694286441Srpaulo * Nothing to do but wait for the init complete notification 2695286441Srpaulo * from the firmware 2696286441Srpaulo */ 2697303628Ssbruno while (!sc->sc_init_complete) { 2698303628Ssbruno error = msleep(&sc->sc_init_complete, &sc->sc_mtx, 2699303628Ssbruno 0, "iwminit", 2*hz); 2700303628Ssbruno if (error) { 2701303628Ssbruno device_printf(sc->sc_dev, "init complete failed: %d\n", 2702303628Ssbruno sc->sc_init_complete); 2703286441Srpaulo break; 2704303628Ssbruno } 2705303628Ssbruno } 2706286441Srpaulo 2707303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, "init %scomplete\n", 2708303628Ssbruno sc->sc_init_complete ? "" : "not "); 2709303628Ssbruno 2710286441Srpaulo return error; 2711286441Srpaulo} 2712286441Srpaulo 2713286441Srpaulo/* 2714286441Srpaulo * receive side 2715286441Srpaulo */ 2716286441Srpaulo 2717286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */ 2718286441Srpaulostatic int 2719286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) 2720286441Srpaulo{ 2721286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 2722286441Srpaulo struct iwm_rx_data *data = &ring->data[idx]; 2723286441Srpaulo struct mbuf *m; 2724301845Sadrian bus_dmamap_t dmamap = NULL; 2725303628Ssbruno bus_dma_segment_t seg; 2726303628Ssbruno int nsegs, error; 2727286441Srpaulo 2728286441Srpaulo m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); 2729286441Srpaulo if (m == NULL) 2730286441Srpaulo return ENOBUFS; 2731286441Srpaulo 2732286441Srpaulo m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; 2733303628Ssbruno error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m, 2734303628Ssbruno &seg, &nsegs, BUS_DMA_NOWAIT); 2735303628Ssbruno if (error != 0) { 2736286441Srpaulo device_printf(sc->sc_dev, 2737301845Sadrian "%s: can't map mbuf, error %d\n", __func__, error); 2738286441Srpaulo goto fail; 2739286441Srpaulo } 2740301845Sadrian 2741301845Sadrian if (data->m != NULL) 2742301845Sadrian bus_dmamap_unload(ring->data_dmat, data->map); 2743301845Sadrian 2744301845Sadrian /* Swap ring->spare_map with data->map */ 2745301845Sadrian dmamap = data->map; 2746301845Sadrian data->map = ring->spare_map; 2747301845Sadrian ring->spare_map = dmamap; 2748301845Sadrian 2749286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD); 2750301845Sadrian data->m = m; 2751286441Srpaulo 2752286441Srpaulo /* Update RX descriptor. */ 2753303628Ssbruno KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned")); 2754303628Ssbruno ring->desc[idx] = htole32(seg.ds_addr >> 8); 2755286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2756286441Srpaulo BUS_DMASYNC_PREWRITE); 2757286441Srpaulo 2758286441Srpaulo return 0; 2759286441Srpaulofail: 2760303628Ssbruno m_freem(m); 2761286441Srpaulo return error; 2762286441Srpaulo} 2763286441Srpaulo 2764286441Srpaulo/* iwlwifi: mvm/rx.c */ 2765286441Srpaulo#define IWM_RSSI_OFFSET 50 2766286441Srpaulostatic int 2767286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2768286441Srpaulo{ 2769286441Srpaulo int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; 2770286441Srpaulo uint32_t agc_a, agc_b; 2771286441Srpaulo uint32_t val; 2772286441Srpaulo 2773286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); 2774286441Srpaulo agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; 2775286441Srpaulo agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; 2776286441Srpaulo 2777286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); 2778286441Srpaulo rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; 2779286441Srpaulo rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; 2780286441Srpaulo 2781286441Srpaulo /* 2782286441Srpaulo * dBm = rssi dB - agc dB - constant. 2783286441Srpaulo * Higher AGC (higher radio gain) means lower signal. 2784286441Srpaulo */ 2785286441Srpaulo rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; 2786286441Srpaulo rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; 2787286441Srpaulo max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); 2788286441Srpaulo 2789286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2790286441Srpaulo "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", 2791286441Srpaulo rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); 2792286441Srpaulo 2793286441Srpaulo return max_rssi_dbm; 2794286441Srpaulo} 2795286441Srpaulo 2796286441Srpaulo/* iwlwifi: mvm/rx.c */ 2797286441Srpaulo/* 2798286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API 2799286441Srpaulo * values are reported by the fw as positive values - need to negate 2800286441Srpaulo * to obtain their dBM. Account for missing antennas by replacing 0 2801286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value. 2802286441Srpaulo */ 2803286441Srpaulostatic int 2804286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2805286441Srpaulo{ 2806286441Srpaulo int energy_a, energy_b, energy_c, max_energy; 2807286441Srpaulo uint32_t val; 2808286441Srpaulo 2809286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); 2810286441Srpaulo energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> 2811286441Srpaulo IWM_RX_INFO_ENERGY_ANT_A_POS; 2812286441Srpaulo energy_a = energy_a ? -energy_a : -256; 2813286441Srpaulo energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> 2814286441Srpaulo IWM_RX_INFO_ENERGY_ANT_B_POS; 2815286441Srpaulo energy_b = energy_b ? -energy_b : -256; 2816286441Srpaulo energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> 2817286441Srpaulo IWM_RX_INFO_ENERGY_ANT_C_POS; 2818286441Srpaulo energy_c = energy_c ? -energy_c : -256; 2819286441Srpaulo max_energy = MAX(energy_a, energy_b); 2820286441Srpaulo max_energy = MAX(max_energy, energy_c); 2821286441Srpaulo 2822286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2823286441Srpaulo "energy In A %d B %d C %d , and max %d\n", 2824286441Srpaulo energy_a, energy_b, energy_c, max_energy); 2825286441Srpaulo 2826286441Srpaulo return max_energy; 2827286441Srpaulo} 2828286441Srpaulo 2829286441Srpaulostatic void 2830286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, 2831286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2832286441Srpaulo{ 2833286441Srpaulo struct iwm_rx_phy_info *phy_info = (void *)pkt->data; 2834286441Srpaulo 2835286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n"); 2836286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2837286441Srpaulo 2838286441Srpaulo memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); 2839286441Srpaulo} 2840286441Srpaulo 2841286441Srpaulo/* 2842286441Srpaulo * Retrieve the average noise (in dBm) among receivers. 2843286441Srpaulo */ 2844286441Srpaulostatic int 2845330144Seadleriwm_get_noise(struct iwm_softc *sc, 2846330144Seadler const struct iwm_mvm_statistics_rx_non_phy *stats) 2847286441Srpaulo{ 2848286441Srpaulo int i, total, nbant, noise; 2849286441Srpaulo 2850286441Srpaulo total = nbant = noise = 0; 2851286441Srpaulo for (i = 0; i < 3; i++) { 2852286441Srpaulo noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; 2853330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n", 2854330144Seadler __func__, 2855330144Seadler i, 2856330144Seadler noise); 2857330144Seadler 2858286441Srpaulo if (noise) { 2859286441Srpaulo total += noise; 2860286441Srpaulo nbant++; 2861286441Srpaulo } 2862286441Srpaulo } 2863286441Srpaulo 2864330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n", 2865330144Seadler __func__, nbant, total); 2866330144Seadler#if 0 2867286441Srpaulo /* There should be at least one antenna but check anyway. */ 2868286441Srpaulo return (nbant == 0) ? -127 : (total / nbant) - 107; 2869330144Seadler#else 2870330144Seadler /* For now, just hard-code it to -96 to be safe */ 2871330144Seadler return (-96); 2872330144Seadler#endif 2873286441Srpaulo} 2874286441Srpaulo 2875286441Srpaulo/* 2876286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler 2877286441Srpaulo * 2878286441Srpaulo * Handles the actual data of the Rx packet from the fw 2879286441Srpaulo */ 2880286441Srpaulostatic void 2881286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, 2882286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2883286441Srpaulo{ 2884287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2885286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2886286441Srpaulo struct ieee80211_frame *wh; 2887286441Srpaulo struct ieee80211_node *ni; 2888286441Srpaulo struct ieee80211_rx_stats rxs; 2889286441Srpaulo struct mbuf *m; 2890286441Srpaulo struct iwm_rx_phy_info *phy_info; 2891286441Srpaulo struct iwm_rx_mpdu_res_start *rx_res; 2892286441Srpaulo uint32_t len; 2893286441Srpaulo uint32_t rx_pkt_status; 2894286441Srpaulo int rssi; 2895286441Srpaulo 2896286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2897286441Srpaulo 2898286441Srpaulo phy_info = &sc->sc_last_phy_info; 2899286441Srpaulo rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; 2900286441Srpaulo wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); 2901286441Srpaulo len = le16toh(rx_res->byte_count); 2902286441Srpaulo rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); 2903286441Srpaulo 2904286441Srpaulo m = data->m; 2905286441Srpaulo m->m_data = pkt->data + sizeof(*rx_res); 2906286441Srpaulo m->m_pkthdr.len = m->m_len = len; 2907286441Srpaulo 2908286441Srpaulo if (__predict_false(phy_info->cfg_phy_cnt > 20)) { 2909286441Srpaulo device_printf(sc->sc_dev, 2910286441Srpaulo "dsp size out of range [0,20]: %d\n", 2911286441Srpaulo phy_info->cfg_phy_cnt); 2912286441Srpaulo return; 2913286441Srpaulo } 2914286441Srpaulo 2915286441Srpaulo if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || 2916286441Srpaulo !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { 2917286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2918286441Srpaulo "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); 2919286441Srpaulo return; /* drop */ 2920286441Srpaulo } 2921286441Srpaulo 2922286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { 2923286441Srpaulo rssi = iwm_mvm_get_signal_strength(sc, phy_info); 2924286441Srpaulo } else { 2925286441Srpaulo rssi = iwm_mvm_calc_rssi(sc, phy_info); 2926286441Srpaulo } 2927286441Srpaulo 2928330144Seadler /* Note: RSSI is absolute (ie a -ve value) */ 2929330144Seadler if (rssi < IWM_MIN_DBM) 2930330144Seadler rssi = IWM_MIN_DBM; 2931330144Seadler else if (rssi > IWM_MAX_DBM) 2932330144Seadler rssi = IWM_MAX_DBM; 2933330144Seadler 2934330144Seadler /* Map it to relative value */ 2935330144Seadler rssi = rssi - sc->sc_noise; 2936330144Seadler 2937286441Srpaulo /* replenish ring for the buffer we're going to feed to the sharks */ 2938286441Srpaulo if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) { 2939286441Srpaulo device_printf(sc->sc_dev, "%s: unable to add more buffers\n", 2940286441Srpaulo __func__); 2941286441Srpaulo return; 2942286441Srpaulo } 2943286441Srpaulo 2944330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2945330144Seadler "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise); 2946330144Seadler 2947286441Srpaulo ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 2948286441Srpaulo 2949286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2950286441Srpaulo "%s: phy_info: channel=%d, flags=0x%08x\n", 2951286441Srpaulo __func__, 2952286441Srpaulo le16toh(phy_info->channel), 2953286441Srpaulo le16toh(phy_info->phy_flags)); 2954286441Srpaulo 2955286441Srpaulo /* 2956286441Srpaulo * Populate an RX state struct with the provided information. 2957286441Srpaulo */ 2958286441Srpaulo bzero(&rxs, sizeof(rxs)); 2959286441Srpaulo rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; 2960286441Srpaulo rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; 2961286441Srpaulo rxs.c_ieee = le16toh(phy_info->channel); 2962286441Srpaulo if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) { 2963286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); 2964286441Srpaulo } else { 2965286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ); 2966286441Srpaulo } 2967330144Seadler 2968330144Seadler /* rssi is in 1/2db units */ 2969330144Seadler rxs.rssi = rssi * 2; 2970286441Srpaulo rxs.nf = sc->sc_noise; 2971286441Srpaulo 2972286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 2973286441Srpaulo struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; 2974286441Srpaulo 2975286441Srpaulo tap->wr_flags = 0; 2976286441Srpaulo if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) 2977286441Srpaulo tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 2978286441Srpaulo tap->wr_chan_freq = htole16(rxs.c_freq); 2979286441Srpaulo /* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */ 2980286441Srpaulo tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); 2981286441Srpaulo tap->wr_dbm_antsignal = (int8_t)rssi; 2982286441Srpaulo tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; 2983286441Srpaulo tap->wr_tsft = phy_info->system_timestamp; 2984286441Srpaulo switch (phy_info->rate) { 2985286441Srpaulo /* CCK rates. */ 2986286441Srpaulo case 10: tap->wr_rate = 2; break; 2987286441Srpaulo case 20: tap->wr_rate = 4; break; 2988286441Srpaulo case 55: tap->wr_rate = 11; break; 2989286441Srpaulo case 110: tap->wr_rate = 22; break; 2990286441Srpaulo /* OFDM rates. */ 2991286441Srpaulo case 0xd: tap->wr_rate = 12; break; 2992286441Srpaulo case 0xf: tap->wr_rate = 18; break; 2993286441Srpaulo case 0x5: tap->wr_rate = 24; break; 2994286441Srpaulo case 0x7: tap->wr_rate = 36; break; 2995286441Srpaulo case 0x9: tap->wr_rate = 48; break; 2996286441Srpaulo case 0xb: tap->wr_rate = 72; break; 2997286441Srpaulo case 0x1: tap->wr_rate = 96; break; 2998286441Srpaulo case 0x3: tap->wr_rate = 108; break; 2999286441Srpaulo /* Unknown rate: should not happen. */ 3000286441Srpaulo default: tap->wr_rate = 0; 3001286441Srpaulo } 3002286441Srpaulo } 3003286441Srpaulo 3004286441Srpaulo IWM_UNLOCK(sc); 3005286441Srpaulo if (ni != NULL) { 3006286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m); 3007286441Srpaulo ieee80211_input_mimo(ni, m, &rxs); 3008286441Srpaulo ieee80211_free_node(ni); 3009286441Srpaulo } else { 3010286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m); 3011286441Srpaulo ieee80211_input_mimo_all(ic, m, &rxs); 3012286441Srpaulo } 3013286441Srpaulo IWM_LOCK(sc); 3014286441Srpaulo} 3015286441Srpaulo 3016293100Savosstatic int 3017286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, 3018286441Srpaulo struct iwm_node *in) 3019286441Srpaulo{ 3020286441Srpaulo struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; 3021293100Savos struct ieee80211_node *ni = &in->in_ni; 3022293100Savos struct ieee80211vap *vap = ni->ni_vap; 3023286441Srpaulo int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; 3024286441Srpaulo int failack = tx_resp->failure_frame; 3025286441Srpaulo 3026286441Srpaulo KASSERT(tx_resp->frame_count == 1, ("too many frames")); 3027286441Srpaulo 3028286441Srpaulo /* Update rate control statistics. */ 3029298611Sadrian 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", 3030298611Sadrian __func__, 3031298611Sadrian (int) le16toh(tx_resp->status.status), 3032298611Sadrian (int) le16toh(tx_resp->status.sequence), 3033298611Sadrian tx_resp->frame_count, 3034298611Sadrian tx_resp->bt_kill_count, 3035298611Sadrian tx_resp->failure_rts, 3036298611Sadrian tx_resp->failure_frame, 3037298611Sadrian le32toh(tx_resp->initial_rate), 3038298611Sadrian (int) le16toh(tx_resp->wireless_media_time)); 3039298611Sadrian 3040286441Srpaulo if (status != IWM_TX_STATUS_SUCCESS && 3041286441Srpaulo status != IWM_TX_STATUS_DIRECT_DONE) { 3042293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3043286441Srpaulo IEEE80211_RATECTL_TX_FAILURE, &failack, NULL); 3044293100Savos return (1); 3045286441Srpaulo } else { 3046293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3047286441Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL); 3048293100Savos return (0); 3049286441Srpaulo } 3050286441Srpaulo} 3051286441Srpaulo 3052286441Srpaulostatic void 3053286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc, 3054286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 3055286441Srpaulo{ 3056286441Srpaulo struct iwm_cmd_header *cmd_hdr = &pkt->hdr; 3057286441Srpaulo int idx = cmd_hdr->idx; 3058286441Srpaulo int qid = cmd_hdr->qid; 3059286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[qid]; 3060286441Srpaulo struct iwm_tx_data *txd = &ring->data[idx]; 3061286441Srpaulo struct iwm_node *in = txd->in; 3062293100Savos struct mbuf *m = txd->m; 3063293100Savos int status; 3064286441Srpaulo 3065293100Savos KASSERT(txd->done == 0, ("txd not done")); 3066293100Savos KASSERT(txd->in != NULL, ("txd without node")); 3067293100Savos KASSERT(txd->m != NULL, ("txd without mbuf")); 3068293100Savos 3069286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 3070286441Srpaulo 3071286441Srpaulo sc->sc_tx_timer = 0; 3072286441Srpaulo 3073293100Savos status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in); 3074286441Srpaulo 3075286441Srpaulo /* Unmap and free mbuf. */ 3076286441Srpaulo bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE); 3077286441Srpaulo bus_dmamap_unload(ring->data_dmat, txd->map); 3078286441Srpaulo 3079286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3080286441Srpaulo "free txd %p, in %p\n", txd, txd->in); 3081286441Srpaulo txd->done = 1; 3082286441Srpaulo txd->m = NULL; 3083286441Srpaulo txd->in = NULL; 3084286441Srpaulo 3085293100Savos ieee80211_tx_complete(&in->in_ni, m, status); 3086293100Savos 3087286441Srpaulo if (--ring->queued < IWM_TX_RING_LOMARK) { 3088286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 3089287197Sglebius if (sc->qfullmsk == 0) { 3090287197Sglebius iwm_start(sc); 3091286441Srpaulo } 3092286441Srpaulo } 3093286441Srpaulo} 3094286441Srpaulo 3095286441Srpaulo/* 3096286441Srpaulo * transmit side 3097286441Srpaulo */ 3098286441Srpaulo 3099286441Srpaulo/* 3100286441Srpaulo * Process a "command done" firmware notification. This is where we wakeup 3101286441Srpaulo * processes waiting for a synchronous command completion. 3102286441Srpaulo * from if_iwn 3103286441Srpaulo */ 3104286441Srpaulostatic void 3105286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3106286441Srpaulo{ 3107286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; 3108286441Srpaulo struct iwm_tx_data *data; 3109286441Srpaulo 3110286441Srpaulo if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { 3111286441Srpaulo return; /* Not a command ack. */ 3112286441Srpaulo } 3113286441Srpaulo 3114330142Seadler /* XXX wide commands? */ 3115330142Seadler IWM_DPRINTF(sc, IWM_DEBUG_CMD, 3116330142Seadler "cmd notification type 0x%x qid %d idx %d\n", 3117330142Seadler pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx); 3118330142Seadler 3119286441Srpaulo data = &ring->data[pkt->hdr.idx]; 3120286441Srpaulo 3121286441Srpaulo /* If the command was mapped in an mbuf, free it. */ 3122286441Srpaulo if (data->m != NULL) { 3123286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3124286441Srpaulo BUS_DMASYNC_POSTWRITE); 3125286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 3126286441Srpaulo m_freem(data->m); 3127286441Srpaulo data->m = NULL; 3128286441Srpaulo } 3129286441Srpaulo wakeup(&ring->desc[pkt->hdr.idx]); 3130286441Srpaulo} 3131286441Srpaulo 3132286441Srpaulo#if 0 3133286441Srpaulo/* 3134286441Srpaulo * necessary only for block ack mode 3135286441Srpaulo */ 3136286441Srpaulovoid 3137286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, 3138286441Srpaulo uint16_t len) 3139286441Srpaulo{ 3140286441Srpaulo struct iwm_agn_scd_bc_tbl *scd_bc_tbl; 3141286441Srpaulo uint16_t w_val; 3142286441Srpaulo 3143286441Srpaulo scd_bc_tbl = sc->sched_dma.vaddr; 3144286441Srpaulo 3145286441Srpaulo len += 8; /* magic numbers came naturally from paris */ 3146286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) 3147286441Srpaulo len = roundup(len, 4) / 4; 3148286441Srpaulo 3149286441Srpaulo w_val = htole16(sta_id << 12 | len); 3150286441Srpaulo 3151286441Srpaulo /* Update TX scheduler. */ 3152286441Srpaulo scd_bc_tbl[qid].tfd_offset[idx] = w_val; 3153286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3154286441Srpaulo BUS_DMASYNC_PREWRITE); 3155286441Srpaulo 3156286441Srpaulo /* I really wonder what this is ?!? */ 3157286441Srpaulo if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { 3158286441Srpaulo scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; 3159286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3160286441Srpaulo BUS_DMASYNC_PREWRITE); 3161286441Srpaulo } 3162286441Srpaulo} 3163286441Srpaulo#endif 3164286441Srpaulo 3165286441Srpaulo/* 3166286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate 3167286441Srpaulo * table entry. return the index into in_ridx[]. 3168286441Srpaulo * 3169286441Srpaulo * The caller then uses that index back into in_ridx 3170286441Srpaulo * to figure out the rate index programmed /into/ 3171286441Srpaulo * the firmware for this given node. 3172286441Srpaulo */ 3173286441Srpaulostatic int 3174286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, 3175286441Srpaulo uint8_t rate) 3176286441Srpaulo{ 3177286441Srpaulo int i; 3178286441Srpaulo uint8_t r; 3179286441Srpaulo 3180286441Srpaulo for (i = 0; i < nitems(in->in_ridx); i++) { 3181286441Srpaulo r = iwm_rates[in->in_ridx[i]].rate; 3182286441Srpaulo if (rate == r) 3183286441Srpaulo return (i); 3184286441Srpaulo } 3185286441Srpaulo /* XXX Return the first */ 3186286441Srpaulo /* XXX TODO: have it return the /lowest/ */ 3187286441Srpaulo return (0); 3188286441Srpaulo} 3189286441Srpaulo 3190286441Srpaulo/* 3191298875Sadrian * Fill in the rate related information for a transmit command. 3192286441Srpaulo */ 3193286441Srpaulostatic const struct iwm_rate * 3194286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, 3195330155Seadler struct mbuf *m, struct iwm_tx_cmd *tx) 3196286441Srpaulo{ 3197286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 3198330155Seadler struct ieee80211_frame *wh; 3199330155Seadler const struct ieee80211_txparam *tp = ni->ni_txparms; 3200286441Srpaulo const struct iwm_rate *rinfo; 3201330155Seadler int type; 3202330155Seadler int ridx, rate_flags, i; 3203286441Srpaulo 3204330155Seadler wh = mtod(m, struct ieee80211_frame *); 3205330155Seadler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3206330155Seadler 3207286441Srpaulo tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; 3208286441Srpaulo tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; 3209286441Srpaulo 3210330155Seadler if (type == IEEE80211_FC0_TYPE_MGT) { 3211330155Seadler i = iwm_tx_rateidx_lookup(sc, in, tp->mgmtrate); 3212330155Seadler ridx = in->in_ridx[i]; 3213330155Seadler } else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3214330155Seadler i = iwm_tx_rateidx_lookup(sc, in, tp->mcastrate); 3215330155Seadler ridx = in->in_ridx[i]; 3216330155Seadler } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 3217330155Seadler i = iwm_tx_rateidx_lookup(sc, in, tp->ucastrate); 3218330155Seadler ridx = in->in_ridx[i]; 3219330155Seadler } else if (m->m_flags & M_EAPOL) { 3220330155Seadler i = iwm_tx_rateidx_lookup(sc, in, tp->mgmtrate); 3221330155Seadler ridx = in->in_ridx[i]; 3222330155Seadler } else { 3223286441Srpaulo /* for data frames, use RS table */ 3224330155Seadler /* XXX pass pktlen */ 3225286441Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 3226286441Srpaulo i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); 3227286441Srpaulo ridx = in->in_ridx[i]; 3228286441Srpaulo 3229286441Srpaulo /* This is the index into the programmed table */ 3230286441Srpaulo tx->initial_rate_index = i; 3231286441Srpaulo tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); 3232286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3233286441Srpaulo "%s: start with i=%d, txrate %d\n", 3234286441Srpaulo __func__, i, iwm_rates[ridx].rate); 3235286441Srpaulo } 3236286441Srpaulo 3237286441Srpaulo rinfo = &iwm_rates[ridx]; 3238286441Srpaulo 3239286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n", 3240286441Srpaulo __func__, ridx, 3241286441Srpaulo rinfo->rate, 3242286441Srpaulo !! (IWM_RIDX_IS_CCK(ridx)) 3243286441Srpaulo ); 3244286441Srpaulo 3245286441Srpaulo /* XXX TODO: hard-coded TX antenna? */ 3246286441Srpaulo rate_flags = 1 << IWM_RATE_MCS_ANT_POS; 3247286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 3248286441Srpaulo rate_flags |= IWM_RATE_MCS_CCK_MSK; 3249286441Srpaulo tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); 3250286441Srpaulo 3251286441Srpaulo return rinfo; 3252286441Srpaulo} 3253286441Srpaulo 3254286441Srpaulo#define TB0_SIZE 16 3255286441Srpaulostatic int 3256286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) 3257286441Srpaulo{ 3258287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3259286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3260293099Savos struct iwm_node *in = IWM_NODE(ni); 3261286441Srpaulo struct iwm_tx_ring *ring; 3262286441Srpaulo struct iwm_tx_data *data; 3263286441Srpaulo struct iwm_tfd *desc; 3264286441Srpaulo struct iwm_device_cmd *cmd; 3265286441Srpaulo struct iwm_tx_cmd *tx; 3266286441Srpaulo struct ieee80211_frame *wh; 3267286441Srpaulo struct ieee80211_key *k = NULL; 3268286441Srpaulo struct mbuf *m1; 3269286441Srpaulo const struct iwm_rate *rinfo; 3270286441Srpaulo uint32_t flags; 3271286441Srpaulo u_int hdrlen; 3272286441Srpaulo bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER]; 3273286441Srpaulo int nsegs; 3274286441Srpaulo uint8_t tid, type; 3275286441Srpaulo int i, totlen, error, pad; 3276286441Srpaulo 3277286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3278286441Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3279286441Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3280286441Srpaulo tid = 0; 3281286441Srpaulo ring = &sc->txq[ac]; 3282286441Srpaulo desc = &ring->desc[ring->cur]; 3283286441Srpaulo memset(desc, 0, sizeof(*desc)); 3284286441Srpaulo data = &ring->data[ring->cur]; 3285286441Srpaulo 3286286441Srpaulo /* Fill out iwm_tx_cmd to send to the firmware */ 3287286441Srpaulo cmd = &ring->cmd[ring->cur]; 3288286441Srpaulo cmd->hdr.code = IWM_TX_CMD; 3289286441Srpaulo cmd->hdr.flags = 0; 3290286441Srpaulo cmd->hdr.qid = ring->qid; 3291286441Srpaulo cmd->hdr.idx = ring->cur; 3292286441Srpaulo 3293286441Srpaulo tx = (void *)cmd->data; 3294286441Srpaulo memset(tx, 0, sizeof(*tx)); 3295286441Srpaulo 3296330155Seadler rinfo = iwm_tx_fill_cmd(sc, in, m, tx); 3297286441Srpaulo 3298286441Srpaulo /* Encrypt the frame if need be. */ 3299286441Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 3300286441Srpaulo /* Retrieve key for TX && do software encryption. */ 3301286441Srpaulo k = ieee80211_crypto_encap(ni, m); 3302286441Srpaulo if (k == NULL) { 3303286441Srpaulo m_freem(m); 3304286441Srpaulo return (ENOBUFS); 3305286441Srpaulo } 3306286441Srpaulo /* 802.11 header may have moved. */ 3307286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3308286441Srpaulo } 3309286441Srpaulo 3310286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3311286441Srpaulo struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; 3312286441Srpaulo 3313286441Srpaulo tap->wt_flags = 0; 3314286441Srpaulo tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); 3315286441Srpaulo tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); 3316286441Srpaulo tap->wt_rate = rinfo->rate; 3317286441Srpaulo if (k != NULL) 3318286441Srpaulo tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 3319286441Srpaulo ieee80211_radiotap_tx(vap, m); 3320286441Srpaulo } 3321286441Srpaulo 3322286441Srpaulo 3323286441Srpaulo totlen = m->m_pkthdr.len; 3324286441Srpaulo 3325286441Srpaulo flags = 0; 3326286441Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3327286441Srpaulo flags |= IWM_TX_CMD_FLG_ACK; 3328286441Srpaulo } 3329286441Srpaulo 3330303628Ssbruno if (type == IEEE80211_FC0_TYPE_DATA 3331286441Srpaulo && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) 3332286441Srpaulo && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3333286441Srpaulo flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; 3334286441Srpaulo } 3335286441Srpaulo 3336286441Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3337286441Srpaulo type != IEEE80211_FC0_TYPE_DATA) 3338286441Srpaulo tx->sta_id = sc->sc_aux_sta.sta_id; 3339286441Srpaulo else 3340286441Srpaulo tx->sta_id = IWM_STATION_ID; 3341286441Srpaulo 3342286441Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 3343286441Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3344286441Srpaulo 3345286441Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3346303628Ssbruno subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 3347303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC); 3348303628Ssbruno } else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) { 3349303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3350303628Ssbruno } else { 3351303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT); 3352303628Ssbruno } 3353286441Srpaulo } else { 3354303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3355286441Srpaulo } 3356286441Srpaulo 3357286441Srpaulo if (hdrlen & 3) { 3358286441Srpaulo /* First segment length must be a multiple of 4. */ 3359286441Srpaulo flags |= IWM_TX_CMD_FLG_MH_PAD; 3360286441Srpaulo pad = 4 - (hdrlen & 3); 3361286441Srpaulo } else 3362286441Srpaulo pad = 0; 3363286441Srpaulo 3364286441Srpaulo tx->driver_txop = 0; 3365286441Srpaulo tx->next_frame_len = 0; 3366286441Srpaulo 3367286441Srpaulo tx->len = htole16(totlen); 3368286441Srpaulo tx->tid_tspec = tid; 3369286441Srpaulo tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); 3370286441Srpaulo 3371286441Srpaulo /* Set physical address of "scratch area". */ 3372286441Srpaulo tx->dram_lsb_ptr = htole32(data->scratch_paddr); 3373286441Srpaulo tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); 3374286441Srpaulo 3375286441Srpaulo /* Copy 802.11 header in TX command. */ 3376286441Srpaulo memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); 3377286441Srpaulo 3378286441Srpaulo flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; 3379286441Srpaulo 3380286441Srpaulo tx->sec_ctl = 0; 3381286441Srpaulo tx->tx_flags |= htole32(flags); 3382286441Srpaulo 3383286441Srpaulo /* Trim 802.11 header. */ 3384286441Srpaulo m_adj(m, hdrlen); 3385286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3386286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3387286441Srpaulo if (error != 0) { 3388286441Srpaulo if (error != EFBIG) { 3389286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3390286441Srpaulo error); 3391286441Srpaulo m_freem(m); 3392286441Srpaulo return error; 3393286441Srpaulo } 3394286441Srpaulo /* Too many DMA segments, linearize mbuf. */ 3395293119Savos m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2); 3396286441Srpaulo if (m1 == NULL) { 3397293119Savos device_printf(sc->sc_dev, 3398293119Savos "%s: could not defrag mbuf\n", __func__); 3399286441Srpaulo m_freem(m); 3400293119Savos return (ENOBUFS); 3401286441Srpaulo } 3402286441Srpaulo m = m1; 3403293119Savos 3404286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3405286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3406286441Srpaulo if (error != 0) { 3407286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3408286441Srpaulo error); 3409286441Srpaulo m_freem(m); 3410286441Srpaulo return error; 3411286441Srpaulo } 3412286441Srpaulo } 3413286441Srpaulo data->m = m; 3414286441Srpaulo data->in = in; 3415286441Srpaulo data->done = 0; 3416286441Srpaulo 3417286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3418286441Srpaulo "sending txd %p, in %p\n", data, data->in); 3419286441Srpaulo KASSERT(data->in != NULL, ("node is NULL")); 3420286441Srpaulo 3421286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3422303628Ssbruno "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n", 3423298611Sadrian ring->qid, ring->cur, totlen, nsegs, 3424298611Sadrian le32toh(tx->tx_flags), 3425298611Sadrian le32toh(tx->rate_n_flags), 3426303628Ssbruno tx->initial_rate_index 3427298611Sadrian ); 3428286441Srpaulo 3429286441Srpaulo /* Fill TX descriptor. */ 3430286441Srpaulo desc->num_tbs = 2 + nsegs; 3431286441Srpaulo 3432286441Srpaulo desc->tbs[0].lo = htole32(data->cmd_paddr); 3433286441Srpaulo desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3434286441Srpaulo (TB0_SIZE << 4); 3435286441Srpaulo desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); 3436286441Srpaulo desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3437286441Srpaulo ((sizeof(struct iwm_cmd_header) + sizeof(*tx) 3438286441Srpaulo + hdrlen + pad - TB0_SIZE) << 4); 3439286441Srpaulo 3440286441Srpaulo /* Other DMA segments are for data payload. */ 3441286441Srpaulo for (i = 0; i < nsegs; i++) { 3442286441Srpaulo seg = &segs[i]; 3443286441Srpaulo desc->tbs[i+2].lo = htole32(seg->ds_addr); 3444286441Srpaulo desc->tbs[i+2].hi_n_len = \ 3445286441Srpaulo htole16(iwm_get_dma_hi_addr(seg->ds_addr)) 3446286441Srpaulo | ((seg->ds_len) << 4); 3447286441Srpaulo } 3448286441Srpaulo 3449286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3450286441Srpaulo BUS_DMASYNC_PREWRITE); 3451286441Srpaulo bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, 3452286441Srpaulo BUS_DMASYNC_PREWRITE); 3453286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3454286441Srpaulo BUS_DMASYNC_PREWRITE); 3455286441Srpaulo 3456286441Srpaulo#if 0 3457286441Srpaulo iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); 3458286441Srpaulo#endif 3459286441Srpaulo 3460286441Srpaulo /* Kick TX ring. */ 3461286441Srpaulo ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; 3462286441Srpaulo IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3463286441Srpaulo 3464286441Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3465286441Srpaulo if (++ring->queued > IWM_TX_RING_HIMARK) { 3466286441Srpaulo sc->qfullmsk |= 1 << ring->qid; 3467286441Srpaulo } 3468286441Srpaulo 3469286441Srpaulo return 0; 3470286441Srpaulo} 3471286441Srpaulo 3472286441Srpaulostatic int 3473286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 3474286441Srpaulo const struct ieee80211_bpf_params *params) 3475286441Srpaulo{ 3476286441Srpaulo struct ieee80211com *ic = ni->ni_ic; 3477286865Sadrian struct iwm_softc *sc = ic->ic_softc; 3478286441Srpaulo int error = 0; 3479286441Srpaulo 3480286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3481286441Srpaulo "->%s begin\n", __func__); 3482286441Srpaulo 3483287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 3484286441Srpaulo m_freem(m); 3485286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3486286441Srpaulo "<-%s not RUNNING\n", __func__); 3487286441Srpaulo return (ENETDOWN); 3488286441Srpaulo } 3489286441Srpaulo 3490286441Srpaulo IWM_LOCK(sc); 3491286441Srpaulo /* XXX fix this */ 3492286441Srpaulo if (params == NULL) { 3493286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3494286441Srpaulo } else { 3495286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3496286441Srpaulo } 3497286441Srpaulo sc->sc_tx_timer = 5; 3498286441Srpaulo IWM_UNLOCK(sc); 3499286441Srpaulo 3500286441Srpaulo return (error); 3501286441Srpaulo} 3502286441Srpaulo 3503286441Srpaulo/* 3504286441Srpaulo * mvm/tx.c 3505286441Srpaulo */ 3506286441Srpaulo 3507286441Srpaulo/* 3508286441Srpaulo * Note that there are transports that buffer frames before they reach 3509286441Srpaulo * the firmware. This means that after flush_tx_path is called, the 3510286441Srpaulo * queue might not be empty. The race-free way to handle this is to: 3511286441Srpaulo * 1) set the station as draining 3512286441Srpaulo * 2) flush the Tx path 3513286441Srpaulo * 3) wait for the transport queues to be empty 3514286441Srpaulo */ 3515286441Srpauloint 3516330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags) 3517286441Srpaulo{ 3518330154Seadler int ret; 3519286441Srpaulo struct iwm_tx_path_flush_cmd flush_cmd = { 3520286441Srpaulo .queues_ctl = htole32(tfd_msk), 3521286441Srpaulo .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), 3522286441Srpaulo }; 3523286441Srpaulo 3524330154Seadler ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags, 3525286441Srpaulo sizeof(flush_cmd), &flush_cmd); 3526286441Srpaulo if (ret) 3527286441Srpaulo device_printf(sc->sc_dev, 3528286441Srpaulo "Flushing tx queue failed: %d\n", ret); 3529286441Srpaulo return ret; 3530286441Srpaulo} 3531286441Srpaulo 3532286441Srpaulo/* 3533286441Srpaulo * BEGIN mvm/sta.c 3534286441Srpaulo */ 3535286441Srpaulo 3536286441Srpaulostatic int 3537286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, 3538303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status) 3539286441Srpaulo{ 3540303628Ssbruno return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd), 3541303628Ssbruno cmd, status); 3542286441Srpaulo} 3543286441Srpaulo 3544286441Srpaulo/* send station add/update command to firmware */ 3545286441Srpaulostatic int 3546286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) 3547286441Srpaulo{ 3548303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 add_sta_cmd; 3549286441Srpaulo int ret; 3550286441Srpaulo uint32_t status; 3551286441Srpaulo 3552286441Srpaulo memset(&add_sta_cmd, 0, sizeof(add_sta_cmd)); 3553286441Srpaulo 3554286441Srpaulo add_sta_cmd.sta_id = IWM_STATION_ID; 3555286441Srpaulo add_sta_cmd.mac_id_n_color 3556286441Srpaulo = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, 3557286441Srpaulo IWM_DEFAULT_COLOR)); 3558286441Srpaulo if (!update) { 3559303628Ssbruno int ac; 3560303628Ssbruno for (ac = 0; ac < WME_NUM_AC; ac++) { 3561303628Ssbruno add_sta_cmd.tfd_queue_msk |= 3562303628Ssbruno htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); 3563303628Ssbruno } 3564286441Srpaulo IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); 3565286441Srpaulo } 3566286441Srpaulo add_sta_cmd.add_modify = update ? 1 : 0; 3567286441Srpaulo add_sta_cmd.station_flags_msk 3568286441Srpaulo |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); 3569303628Ssbruno add_sta_cmd.tid_disable_tx = htole16(0xffff); 3570303628Ssbruno if (update) 3571303628Ssbruno add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX); 3572286441Srpaulo 3573286441Srpaulo status = IWM_ADD_STA_SUCCESS; 3574286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status); 3575286441Srpaulo if (ret) 3576286441Srpaulo return ret; 3577286441Srpaulo 3578286441Srpaulo switch (status) { 3579286441Srpaulo case IWM_ADD_STA_SUCCESS: 3580286441Srpaulo break; 3581286441Srpaulo default: 3582286441Srpaulo ret = EIO; 3583286441Srpaulo device_printf(sc->sc_dev, "IWM_ADD_STA failed\n"); 3584286441Srpaulo break; 3585286441Srpaulo } 3586286441Srpaulo 3587286441Srpaulo return ret; 3588286441Srpaulo} 3589286441Srpaulo 3590286441Srpaulostatic int 3591286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) 3592286441Srpaulo{ 3593301192Sadrian return iwm_mvm_sta_send_to_fw(sc, in, 0); 3594286441Srpaulo} 3595286441Srpaulo 3596286441Srpaulostatic int 3597286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) 3598286441Srpaulo{ 3599286441Srpaulo return iwm_mvm_sta_send_to_fw(sc, in, 1); 3600286441Srpaulo} 3601286441Srpaulo 3602286441Srpaulostatic int 3603286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, 3604286441Srpaulo const uint8_t *addr, uint16_t mac_id, uint16_t color) 3605286441Srpaulo{ 3606303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 cmd; 3607286441Srpaulo int ret; 3608286441Srpaulo uint32_t status; 3609286441Srpaulo 3610286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3611286441Srpaulo cmd.sta_id = sta->sta_id; 3612286441Srpaulo cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); 3613286441Srpaulo 3614286441Srpaulo cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); 3615303628Ssbruno cmd.tid_disable_tx = htole16(0xffff); 3616286441Srpaulo 3617286441Srpaulo if (addr) 3618286441Srpaulo IEEE80211_ADDR_COPY(cmd.addr, addr); 3619286441Srpaulo 3620286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); 3621286441Srpaulo if (ret) 3622286441Srpaulo return ret; 3623286441Srpaulo 3624286441Srpaulo switch (status) { 3625286441Srpaulo case IWM_ADD_STA_SUCCESS: 3626286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 3627286441Srpaulo "%s: Internal station added.\n", __func__); 3628286441Srpaulo return 0; 3629286441Srpaulo default: 3630286441Srpaulo device_printf(sc->sc_dev, 3631286441Srpaulo "%s: Add internal station failed, status=0x%x\n", 3632286441Srpaulo __func__, status); 3633286441Srpaulo ret = EIO; 3634286441Srpaulo break; 3635286441Srpaulo } 3636286441Srpaulo return ret; 3637286441Srpaulo} 3638286441Srpaulo 3639286441Srpaulostatic int 3640286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc) 3641286441Srpaulo{ 3642286441Srpaulo int ret; 3643286441Srpaulo 3644303628Ssbruno sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID; 3645303628Ssbruno sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE); 3646286441Srpaulo 3647303628Ssbruno ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST); 3648303628Ssbruno if (ret) 3649303628Ssbruno return ret; 3650303628Ssbruno 3651286441Srpaulo ret = iwm_mvm_add_int_sta_common(sc, 3652286441Srpaulo &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); 3653286441Srpaulo 3654286441Srpaulo if (ret) 3655286441Srpaulo memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta)); 3656286441Srpaulo return ret; 3657286441Srpaulo} 3658286441Srpaulo 3659286441Srpaulo/* 3660286441Srpaulo * END mvm/sta.c 3661286441Srpaulo */ 3662286441Srpaulo 3663286441Srpaulo/* 3664286441Srpaulo * BEGIN mvm/quota.c 3665286441Srpaulo */ 3666286441Srpaulo 3667286441Srpaulostatic int 3668286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in) 3669286441Srpaulo{ 3670286441Srpaulo struct iwm_time_quota_cmd cmd; 3671286441Srpaulo int i, idx, ret, num_active_macs, quota, quota_rem; 3672286441Srpaulo int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, }; 3673286441Srpaulo int n_ifs[IWM_MAX_BINDINGS] = {0, }; 3674286441Srpaulo uint16_t id; 3675286441Srpaulo 3676286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3677286441Srpaulo 3678286441Srpaulo /* currently, PHY ID == binding ID */ 3679286441Srpaulo if (in) { 3680286441Srpaulo id = in->in_phyctxt->id; 3681286441Srpaulo KASSERT(id < IWM_MAX_BINDINGS, ("invalid id")); 3682286441Srpaulo colors[id] = in->in_phyctxt->color; 3683286441Srpaulo 3684286441Srpaulo if (1) 3685286441Srpaulo n_ifs[id] = 1; 3686286441Srpaulo } 3687286441Srpaulo 3688286441Srpaulo /* 3689286441Srpaulo * The FW's scheduling session consists of 3690286441Srpaulo * IWM_MVM_MAX_QUOTA fragments. Divide these fragments 3691286441Srpaulo * equally between all the bindings that require quota 3692286441Srpaulo */ 3693286441Srpaulo num_active_macs = 0; 3694286441Srpaulo for (i = 0; i < IWM_MAX_BINDINGS; i++) { 3695286441Srpaulo cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID); 3696286441Srpaulo num_active_macs += n_ifs[i]; 3697286441Srpaulo } 3698286441Srpaulo 3699286441Srpaulo quota = 0; 3700286441Srpaulo quota_rem = 0; 3701286441Srpaulo if (num_active_macs) { 3702286441Srpaulo quota = IWM_MVM_MAX_QUOTA / num_active_macs; 3703286441Srpaulo quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs; 3704286441Srpaulo } 3705286441Srpaulo 3706286441Srpaulo for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) { 3707286441Srpaulo if (colors[i] < 0) 3708286441Srpaulo continue; 3709286441Srpaulo 3710286441Srpaulo cmd.quotas[idx].id_and_color = 3711286441Srpaulo htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i])); 3712286441Srpaulo 3713286441Srpaulo if (n_ifs[i] <= 0) { 3714286441Srpaulo cmd.quotas[idx].quota = htole32(0); 3715286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3716286441Srpaulo } else { 3717286441Srpaulo cmd.quotas[idx].quota = htole32(quota * n_ifs[i]); 3718286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3719286441Srpaulo } 3720286441Srpaulo idx++; 3721286441Srpaulo } 3722286441Srpaulo 3723286441Srpaulo /* Give the remainder of the session to the first binding */ 3724286441Srpaulo cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem); 3725286441Srpaulo 3726286441Srpaulo ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC, 3727286441Srpaulo sizeof(cmd), &cmd); 3728286441Srpaulo if (ret) 3729286441Srpaulo device_printf(sc->sc_dev, 3730286441Srpaulo "%s: Failed to send quota: %d\n", __func__, ret); 3731286441Srpaulo return ret; 3732286441Srpaulo} 3733286441Srpaulo 3734286441Srpaulo/* 3735286441Srpaulo * END mvm/quota.c 3736286441Srpaulo */ 3737286441Srpaulo 3738286441Srpaulo/* 3739286441Srpaulo * ieee80211 routines 3740286441Srpaulo */ 3741286441Srpaulo 3742286441Srpaulo/* 3743286441Srpaulo * Change to AUTH state in 80211 state machine. Roughly matches what 3744286441Srpaulo * Linux does in bss_info_changed(). 3745286441Srpaulo */ 3746286441Srpaulostatic int 3747286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) 3748286441Srpaulo{ 3749286441Srpaulo struct ieee80211_node *ni; 3750286441Srpaulo struct iwm_node *in; 3751286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 3752286441Srpaulo uint32_t duration; 3753286441Srpaulo int error; 3754286441Srpaulo 3755286441Srpaulo /* 3756286441Srpaulo * XXX i have a feeling that the vap node is being 3757286441Srpaulo * freed from underneath us. Grr. 3758286441Srpaulo */ 3759286441Srpaulo ni = ieee80211_ref_node(vap->iv_bss); 3760293099Savos in = IWM_NODE(ni); 3761286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE, 3762286441Srpaulo "%s: called; vap=%p, bss ni=%p\n", 3763286441Srpaulo __func__, 3764286441Srpaulo vap, 3765286441Srpaulo ni); 3766286441Srpaulo 3767286441Srpaulo in->in_assoc = 0; 3768286441Srpaulo 3769303628Ssbruno error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON); 3770303628Ssbruno if (error != 0) 3771303628Ssbruno return error; 3772303628Ssbruno 3773286441Srpaulo error = iwm_allow_mcast(vap, sc); 3774286441Srpaulo if (error) { 3775286441Srpaulo device_printf(sc->sc_dev, 3776286441Srpaulo "%s: failed to set multicast\n", __func__); 3777286441Srpaulo goto out; 3778286441Srpaulo } 3779286441Srpaulo 3780286441Srpaulo /* 3781286441Srpaulo * This is where it deviates from what Linux does. 3782286441Srpaulo * 3783286441Srpaulo * Linux iwlwifi doesn't reset the nic each time, nor does it 3784286441Srpaulo * call ctxt_add() here. Instead, it adds it during vap creation, 3785303628Ssbruno * and always does a mac_ctx_changed(). 3786286441Srpaulo * 3787286441Srpaulo * The openbsd port doesn't attempt to do that - it reset things 3788286441Srpaulo * at odd states and does the add here. 3789286441Srpaulo * 3790286441Srpaulo * So, until the state handling is fixed (ie, we never reset 3791286441Srpaulo * the NIC except for a firmware failure, which should drag 3792286441Srpaulo * the NIC back to IDLE, re-setup and re-add all the mac/phy 3793286441Srpaulo * contexts that are required), let's do a dirty hack here. 3794286441Srpaulo */ 3795286441Srpaulo if (iv->is_uploaded) { 3796286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 3797286441Srpaulo device_printf(sc->sc_dev, 3798298582Sadrian "%s: failed to update MAC\n", __func__); 3799286441Srpaulo goto out; 3800286441Srpaulo } 3801298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 3802298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 3803298582Sadrian device_printf(sc->sc_dev, 3804298582Sadrian "%s: failed update phy ctxt\n", __func__); 3805298582Sadrian goto out; 3806298582Sadrian } 3807298582Sadrian in->in_phyctxt = &sc->sc_phyctxt[0]; 3808298582Sadrian 3809298582Sadrian if ((error = iwm_mvm_binding_update(sc, in)) != 0) { 3810298582Sadrian device_printf(sc->sc_dev, 3811298582Sadrian "%s: binding update cmd\n", __func__); 3812298582Sadrian goto out; 3813298582Sadrian } 3814298582Sadrian if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 3815298582Sadrian device_printf(sc->sc_dev, 3816298582Sadrian "%s: failed to update sta\n", __func__); 3817298582Sadrian goto out; 3818298582Sadrian } 3819286441Srpaulo } else { 3820286441Srpaulo if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) { 3821286441Srpaulo device_printf(sc->sc_dev, 3822286441Srpaulo "%s: failed to add MAC\n", __func__); 3823286441Srpaulo goto out; 3824286441Srpaulo } 3825298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 3826298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 3827286441Srpaulo device_printf(sc->sc_dev, 3828298582Sadrian "%s: failed add phy ctxt!\n", __func__); 3829286441Srpaulo error = ETIMEDOUT; 3830286441Srpaulo goto out; 3831298582Sadrian } 3832298582Sadrian in->in_phyctxt = &sc->sc_phyctxt[0]; 3833298582Sadrian 3834298582Sadrian if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) { 3835286441Srpaulo device_printf(sc->sc_dev, 3836298582Sadrian "%s: binding add cmd\n", __func__); 3837286441Srpaulo goto out; 3838286441Srpaulo } 3839298582Sadrian if ((error = iwm_mvm_add_sta(sc, in)) != 0) { 3840298582Sadrian device_printf(sc->sc_dev, 3841298582Sadrian "%s: failed to add sta\n", __func__); 3842298582Sadrian goto out; 3843298582Sadrian } 3844286441Srpaulo } 3845298582Sadrian 3846298582Sadrian /* 3847298582Sadrian * Prevent the FW from wandering off channel during association 3848298582Sadrian * by "protecting" the session with a time event. 3849298582Sadrian */ 3850298582Sadrian /* XXX duration is in units of TU, not MS */ 3851298582Sadrian duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; 3852298582Sadrian iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */); 3853298582Sadrian DELAY(100); 3854298582Sadrian 3855286441Srpaulo error = 0; 3856286441Srpauloout: 3857286441Srpaulo ieee80211_free_node(ni); 3858286441Srpaulo return (error); 3859286441Srpaulo} 3860286441Srpaulo 3861286441Srpaulostatic int 3862286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc) 3863286441Srpaulo{ 3864293099Savos struct iwm_node *in = IWM_NODE(vap->iv_bss); 3865286441Srpaulo int error; 3866286441Srpaulo 3867286441Srpaulo if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 3868286441Srpaulo device_printf(sc->sc_dev, 3869286441Srpaulo "%s: failed to update STA\n", __func__); 3870286441Srpaulo return error; 3871286441Srpaulo } 3872286441Srpaulo 3873286441Srpaulo in->in_assoc = 1; 3874286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 3875286441Srpaulo device_printf(sc->sc_dev, 3876286441Srpaulo "%s: failed to update MAC\n", __func__); 3877286441Srpaulo return error; 3878286441Srpaulo } 3879286441Srpaulo 3880286441Srpaulo return 0; 3881286441Srpaulo} 3882286441Srpaulo 3883286441Srpaulostatic int 3884286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in) 3885286441Srpaulo{ 3886330154Seadler uint32_t tfd_msk; 3887330154Seadler 3888286441Srpaulo /* 3889286441Srpaulo * Ok, so *technically* the proper set of calls for going 3890286441Srpaulo * from RUN back to SCAN is: 3891286441Srpaulo * 3892286441Srpaulo * iwm_mvm_power_mac_disable(sc, in); 3893286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 3894286441Srpaulo * iwm_mvm_rm_sta(sc, in); 3895286441Srpaulo * iwm_mvm_update_quotas(sc, NULL); 3896286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 3897286441Srpaulo * iwm_mvm_binding_remove_vif(sc, in); 3898286441Srpaulo * iwm_mvm_mac_ctxt_remove(sc, in); 3899286441Srpaulo * 3900286441Srpaulo * However, that freezes the device not matter which permutations 3901286441Srpaulo * and modifications are attempted. Obviously, this driver is missing 3902286441Srpaulo * something since it works in the Linux driver, but figuring out what 3903286441Srpaulo * is missing is a little more complicated. Now, since we're going 3904286441Srpaulo * back to nothing anyway, we'll just do a complete device reset. 3905286441Srpaulo * Up your's, device! 3906286441Srpaulo */ 3907330154Seadler /* 3908330154Seadler * Just using 0xf for the queues mask is fine as long as we only 3909330154Seadler * get here from RUN state. 3910330154Seadler */ 3911330154Seadler tfd_msk = 0xf; 3912330154Seadler mbufq_drain(&sc->sc_snd); 3913330154Seadler iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC); 3914330154Seadler /* 3915330154Seadler * We seem to get away with just synchronously sending the 3916330154Seadler * IWM_TXPATH_FLUSH command. 3917330154Seadler */ 3918330154Seadler// iwm_trans_wait_tx_queue_empty(sc, tfd_msk); 3919286441Srpaulo iwm_stop_device(sc); 3920286441Srpaulo iwm_init_hw(sc); 3921286441Srpaulo if (in) 3922286441Srpaulo in->in_assoc = 0; 3923286441Srpaulo return 0; 3924286441Srpaulo 3925286441Srpaulo#if 0 3926286441Srpaulo int error; 3927286441Srpaulo 3928286441Srpaulo iwm_mvm_power_mac_disable(sc, in); 3929286441Srpaulo 3930286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 3931286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); 3932286441Srpaulo return error; 3933286441Srpaulo } 3934286441Srpaulo 3935286441Srpaulo if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { 3936286441Srpaulo device_printf(sc->sc_dev, "sta remove fail %d\n", error); 3937286441Srpaulo return error; 3938286441Srpaulo } 3939286441Srpaulo error = iwm_mvm_rm_sta(sc, in); 3940286441Srpaulo in->in_assoc = 0; 3941286441Srpaulo iwm_mvm_update_quotas(sc, NULL); 3942286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 3943286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); 3944286441Srpaulo return error; 3945286441Srpaulo } 3946286441Srpaulo iwm_mvm_binding_remove_vif(sc, in); 3947286441Srpaulo 3948286441Srpaulo iwm_mvm_mac_ctxt_remove(sc, in); 3949286441Srpaulo 3950286441Srpaulo return error; 3951286441Srpaulo#endif 3952286441Srpaulo} 3953286441Srpaulo 3954286441Srpaulostatic struct ieee80211_node * 3955286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 3956286441Srpaulo{ 3957286441Srpaulo return malloc(sizeof (struct iwm_node), M_80211_NODE, 3958286441Srpaulo M_NOWAIT | M_ZERO); 3959286441Srpaulo} 3960286441Srpaulo 3961286441Srpaulostatic void 3962286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in) 3963286441Srpaulo{ 3964286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 3965286441Srpaulo struct iwm_lq_cmd *lq = &in->in_lq; 3966286441Srpaulo int nrates = ni->ni_rates.rs_nrates; 3967286441Srpaulo int i, ridx, tab = 0; 3968286441Srpaulo int txant = 0; 3969286441Srpaulo 3970286441Srpaulo if (nrates > nitems(lq->rs_table)) { 3971286441Srpaulo device_printf(sc->sc_dev, 3972286441Srpaulo "%s: node supports %d rates, driver handles " 3973286441Srpaulo "only %zu\n", __func__, nrates, nitems(lq->rs_table)); 3974286441Srpaulo return; 3975286441Srpaulo } 3976294248Sadrian if (nrates == 0) { 3977294248Sadrian device_printf(sc->sc_dev, 3978294248Sadrian "%s: node supports 0 rates, odd!\n", __func__); 3979294248Sadrian return; 3980294248Sadrian } 3981286441Srpaulo 3982286441Srpaulo /* 3983286441Srpaulo * XXX .. and most of iwm_node is not initialised explicitly; 3984286441Srpaulo * it's all just 0x0 passed to the firmware. 3985286441Srpaulo */ 3986286441Srpaulo 3987286441Srpaulo /* first figure out which rates we should support */ 3988286441Srpaulo /* XXX TODO: this isn't 11n aware /at all/ */ 3989286441Srpaulo memset(&in->in_ridx, -1, sizeof(in->in_ridx)); 3990286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3991286441Srpaulo "%s: nrates=%d\n", __func__, nrates); 3992286441Srpaulo 3993294248Sadrian /* 3994294248Sadrian * Loop over nrates and populate in_ridx from the highest 3995294248Sadrian * rate to the lowest rate. Remember, in_ridx[] has 3996294248Sadrian * IEEE80211_RATE_MAXSIZE entries! 3997294248Sadrian */ 3998294248Sadrian for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) { 3999294248Sadrian int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL; 4000294248Sadrian 4001286441Srpaulo /* Map 802.11 rate to HW rate index. */ 4002286441Srpaulo for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) 4003286441Srpaulo if (iwm_rates[ridx].rate == rate) 4004286441Srpaulo break; 4005286441Srpaulo if (ridx > IWM_RIDX_MAX) { 4006286441Srpaulo device_printf(sc->sc_dev, 4007286441Srpaulo "%s: WARNING: device rate for %d not found!\n", 4008286441Srpaulo __func__, rate); 4009286441Srpaulo } else { 4010286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4011286441Srpaulo "%s: rate: i: %d, rate=%d, ridx=%d\n", 4012286441Srpaulo __func__, 4013286441Srpaulo i, 4014286441Srpaulo rate, 4015286441Srpaulo ridx); 4016286441Srpaulo in->in_ridx[i] = ridx; 4017286441Srpaulo } 4018286441Srpaulo } 4019286441Srpaulo 4020286441Srpaulo /* then construct a lq_cmd based on those */ 4021286441Srpaulo memset(lq, 0, sizeof(*lq)); 4022286441Srpaulo lq->sta_id = IWM_STATION_ID; 4023286441Srpaulo 4024303628Ssbruno /* For HT, always enable RTS/CTS to avoid excessive retries. */ 4025303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) 4026303628Ssbruno lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; 4027303628Ssbruno 4028286441Srpaulo /* 4029286441Srpaulo * are these used? (we don't do SISO or MIMO) 4030286441Srpaulo * need to set them to non-zero, though, or we get an error. 4031286441Srpaulo */ 4032286441Srpaulo lq->single_stream_ant_msk = 1; 4033286441Srpaulo lq->dual_stream_ant_msk = 1; 4034286441Srpaulo 4035286441Srpaulo /* 4036286441Srpaulo * Build the actual rate selection table. 4037286441Srpaulo * The lowest bits are the rates. Additionally, 4038286441Srpaulo * CCK needs bit 9 to be set. The rest of the bits 4039286441Srpaulo * we add to the table select the tx antenna 4040286441Srpaulo * Note that we add the rates in the highest rate first 4041286441Srpaulo * (opposite of ni_rates). 4042286441Srpaulo */ 4043286441Srpaulo /* 4044286441Srpaulo * XXX TODO: this should be looping over the min of nrates 4045286441Srpaulo * and LQ_MAX_RETRY_NUM. Sigh. 4046286441Srpaulo */ 4047286441Srpaulo for (i = 0; i < nrates; i++) { 4048286441Srpaulo int nextant; 4049286441Srpaulo 4050286441Srpaulo if (txant == 0) 4051303628Ssbruno txant = iwm_fw_valid_tx_ant(sc); 4052286441Srpaulo nextant = 1<<(ffs(txant)-1); 4053286441Srpaulo txant &= ~nextant; 4054286441Srpaulo 4055286441Srpaulo /* 4056286441Srpaulo * Map the rate id into a rate index into 4057286441Srpaulo * our hardware table containing the 4058286441Srpaulo * configuration to use for this rate. 4059286441Srpaulo */ 4060294248Sadrian ridx = in->in_ridx[i]; 4061286441Srpaulo tab = iwm_rates[ridx].plcp; 4062286441Srpaulo tab |= nextant << IWM_RATE_MCS_ANT_POS; 4063286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 4064286441Srpaulo tab |= IWM_RATE_MCS_CCK_MSK; 4065286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4066286441Srpaulo "station rate i=%d, rate=%d, hw=%x\n", 4067286441Srpaulo i, iwm_rates[ridx].rate, tab); 4068286441Srpaulo lq->rs_table[i] = htole32(tab); 4069286441Srpaulo } 4070286441Srpaulo /* then fill the rest with the lowest possible rate */ 4071286441Srpaulo for (i = nrates; i < nitems(lq->rs_table); i++) { 4072286441Srpaulo KASSERT(tab != 0, ("invalid tab")); 4073286441Srpaulo lq->rs_table[i] = htole32(tab); 4074286441Srpaulo } 4075286441Srpaulo} 4076286441Srpaulo 4077286441Srpaulostatic int 4078286441Srpauloiwm_media_change(struct ifnet *ifp) 4079286441Srpaulo{ 4080287197Sglebius struct ieee80211vap *vap = ifp->if_softc; 4081287197Sglebius struct ieee80211com *ic = vap->iv_ic; 4082287197Sglebius struct iwm_softc *sc = ic->ic_softc; 4083286441Srpaulo int error; 4084286441Srpaulo 4085286441Srpaulo error = ieee80211_media_change(ifp); 4086286441Srpaulo if (error != ENETRESET) 4087286441Srpaulo return error; 4088286441Srpaulo 4089287197Sglebius IWM_LOCK(sc); 4090287197Sglebius if (ic->ic_nrunning > 0) { 4091287197Sglebius iwm_stop(sc); 4092286441Srpaulo iwm_init(sc); 4093286441Srpaulo } 4094287197Sglebius IWM_UNLOCK(sc); 4095286441Srpaulo return error; 4096286441Srpaulo} 4097286441Srpaulo 4098286441Srpaulo 4099286441Srpaulostatic int 4100286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 4101286441Srpaulo{ 4102286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 4103286441Srpaulo struct ieee80211com *ic = vap->iv_ic; 4104286865Sadrian struct iwm_softc *sc = ic->ic_softc; 4105286441Srpaulo struct iwm_node *in; 4106286441Srpaulo int error; 4107286441Srpaulo 4108286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4109286441Srpaulo "switching state %s -> %s\n", 4110286441Srpaulo ieee80211_state_name[vap->iv_state], 4111286441Srpaulo ieee80211_state_name[nstate]); 4112286441Srpaulo IEEE80211_UNLOCK(ic); 4113286441Srpaulo IWM_LOCK(sc); 4114301187Sadrian 4115301187Sadrian if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state) 4116301187Sadrian iwm_led_blink_stop(sc); 4117301187Sadrian 4118286441Srpaulo /* disable beacon filtering if we're hopping out of RUN */ 4119286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { 4120286441Srpaulo iwm_mvm_disable_beacon_filter(sc); 4121286441Srpaulo 4122293099Savos if (((in = IWM_NODE(vap->iv_bss)) != NULL)) 4123286441Srpaulo in->in_assoc = 0; 4124286441Srpaulo 4125330154Seadler if (nstate == IEEE80211_S_INIT) { 4126330154Seadler IWM_UNLOCK(sc); 4127330154Seadler IEEE80211_LOCK(ic); 4128330154Seadler error = ivp->iv_newstate(vap, nstate, arg); 4129330154Seadler IEEE80211_UNLOCK(ic); 4130330154Seadler IWM_LOCK(sc); 4131330154Seadler iwm_release(sc, NULL); 4132330154Seadler IWM_UNLOCK(sc); 4133330154Seadler IEEE80211_LOCK(ic); 4134330154Seadler return error; 4135330154Seadler } 4136286441Srpaulo 4137286441Srpaulo /* 4138286441Srpaulo * It's impossible to directly go RUN->SCAN. If we iwm_release() 4139286441Srpaulo * above then the card will be completely reinitialized, 4140286441Srpaulo * so the driver must do everything necessary to bring the card 4141286441Srpaulo * from INIT to SCAN. 4142286441Srpaulo * 4143286441Srpaulo * Additionally, upon receiving deauth frame from AP, 4144286441Srpaulo * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH 4145286441Srpaulo * state. This will also fail with this driver, so bring the FSM 4146286441Srpaulo * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. 4147286441Srpaulo * 4148286441Srpaulo * XXX TODO: fix this for FreeBSD! 4149286441Srpaulo */ 4150286441Srpaulo if (nstate == IEEE80211_S_SCAN || 4151286441Srpaulo nstate == IEEE80211_S_AUTH || 4152286441Srpaulo nstate == IEEE80211_S_ASSOC) { 4153286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4154286441Srpaulo "Force transition to INIT; MGT=%d\n", arg); 4155286441Srpaulo IWM_UNLOCK(sc); 4156286441Srpaulo IEEE80211_LOCK(ic); 4157303628Ssbruno /* Always pass arg as -1 since we can't Tx right now. */ 4158303628Ssbruno /* 4159303628Ssbruno * XXX arg is just ignored anyway when transitioning 4160303628Ssbruno * to IEEE80211_S_INIT. 4161303628Ssbruno */ 4162303628Ssbruno vap->iv_newstate(vap, IEEE80211_S_INIT, -1); 4163286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4164286441Srpaulo "Going INIT->SCAN\n"); 4165286441Srpaulo nstate = IEEE80211_S_SCAN; 4166286441Srpaulo IEEE80211_UNLOCK(ic); 4167286441Srpaulo IWM_LOCK(sc); 4168286441Srpaulo } 4169286441Srpaulo } 4170286441Srpaulo 4171286441Srpaulo switch (nstate) { 4172286441Srpaulo case IEEE80211_S_INIT: 4173286441Srpaulo break; 4174286441Srpaulo 4175286441Srpaulo case IEEE80211_S_AUTH: 4176286441Srpaulo if ((error = iwm_auth(vap, sc)) != 0) { 4177286441Srpaulo device_printf(sc->sc_dev, 4178286441Srpaulo "%s: could not move to auth state: %d\n", 4179286441Srpaulo __func__, error); 4180286441Srpaulo break; 4181286441Srpaulo } 4182286441Srpaulo break; 4183286441Srpaulo 4184286441Srpaulo case IEEE80211_S_ASSOC: 4185286441Srpaulo if ((error = iwm_assoc(vap, sc)) != 0) { 4186286441Srpaulo device_printf(sc->sc_dev, 4187286441Srpaulo "%s: failed to associate: %d\n", __func__, 4188286441Srpaulo error); 4189286441Srpaulo break; 4190286441Srpaulo } 4191286441Srpaulo break; 4192286441Srpaulo 4193286441Srpaulo case IEEE80211_S_RUN: 4194286441Srpaulo { 4195286441Srpaulo struct iwm_host_cmd cmd = { 4196286441Srpaulo .id = IWM_LQ_CMD, 4197286441Srpaulo .len = { sizeof(in->in_lq), }, 4198286441Srpaulo .flags = IWM_CMD_SYNC, 4199286441Srpaulo }; 4200286441Srpaulo 4201286441Srpaulo /* Update the association state, now we have it all */ 4202286441Srpaulo /* (eg associd comes in at this point */ 4203286441Srpaulo error = iwm_assoc(vap, sc); 4204286441Srpaulo if (error != 0) { 4205286441Srpaulo device_printf(sc->sc_dev, 4206286441Srpaulo "%s: failed to update association state: %d\n", 4207286441Srpaulo __func__, 4208286441Srpaulo error); 4209286441Srpaulo break; 4210286441Srpaulo } 4211286441Srpaulo 4212293099Savos in = IWM_NODE(vap->iv_bss); 4213286441Srpaulo iwm_mvm_power_mac_update_mode(sc, in); 4214286441Srpaulo iwm_mvm_enable_beacon_filter(sc, in); 4215286441Srpaulo iwm_mvm_update_quotas(sc, in); 4216286441Srpaulo iwm_setrates(sc, in); 4217286441Srpaulo 4218286441Srpaulo cmd.data[0] = &in->in_lq; 4219286441Srpaulo if ((error = iwm_send_cmd(sc, &cmd)) != 0) { 4220286441Srpaulo device_printf(sc->sc_dev, 4221286441Srpaulo "%s: IWM_LQ_CMD failed\n", __func__); 4222286441Srpaulo } 4223286441Srpaulo 4224303628Ssbruno iwm_mvm_led_enable(sc); 4225286441Srpaulo break; 4226286441Srpaulo } 4227286441Srpaulo 4228286441Srpaulo default: 4229286441Srpaulo break; 4230286441Srpaulo } 4231286441Srpaulo IWM_UNLOCK(sc); 4232286441Srpaulo IEEE80211_LOCK(ic); 4233286441Srpaulo 4234286441Srpaulo return (ivp->iv_newstate(vap, nstate, arg)); 4235286441Srpaulo} 4236286441Srpaulo 4237286441Srpaulovoid 4238286441Srpauloiwm_endscan_cb(void *arg, int pending) 4239286441Srpaulo{ 4240286441Srpaulo struct iwm_softc *sc = arg; 4241287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4242286441Srpaulo 4243286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE, 4244286441Srpaulo "%s: scan ended\n", 4245286441Srpaulo __func__); 4246286441Srpaulo 4247303628Ssbruno ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); 4248303628Ssbruno} 4249303628Ssbruno 4250303628Ssbruno/* 4251303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4252303628Ssbruno * in default configuration 4253303628Ssbruno */ 4254303628Ssbrunostatic const uint32_t 4255303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4256303628Ssbruno { 4257303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF), 4258303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF) 4259303628Ssbruno }, 4260303628Ssbruno { 4261303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF), 4262303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF) 4263303628Ssbruno }, 4264303628Ssbruno { 4265303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER_DEF), 4266303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER_DEF) 4267303628Ssbruno }, 4268303628Ssbruno { 4269303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER_DEF), 4270303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER_DEF) 4271303628Ssbruno }, 4272303628Ssbruno { 4273303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER_DEF), 4274303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF) 4275303628Ssbruno }, 4276303628Ssbruno}; 4277303628Ssbruno 4278303628Ssbruno/* 4279303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4280303628Ssbruno * in single BSS MAC configuration. 4281303628Ssbruno */ 4282303628Ssbrunostatic const uint32_t 4283303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4284303628Ssbruno { 4285303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER), 4286303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER) 4287303628Ssbruno }, 4288303628Ssbruno { 4289303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER), 4290303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER) 4291303628Ssbruno }, 4292303628Ssbruno { 4293303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER), 4294303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER) 4295303628Ssbruno }, 4296303628Ssbruno { 4297303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER), 4298303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER) 4299303628Ssbruno }, 4300303628Ssbruno { 4301303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER), 4302303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER) 4303303628Ssbruno }, 4304303628Ssbruno}; 4305303628Ssbruno 4306303628Ssbrunostatic void 4307303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd, 4308303628Ssbruno struct ieee80211_node *ni) 4309303628Ssbruno{ 4310303628Ssbruno int i, j, watermark; 4311303628Ssbruno 4312303628Ssbruno sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN); 4313303628Ssbruno 4314303628Ssbruno /* 4315303628Ssbruno * If we are in association flow - check antenna configuration 4316303628Ssbruno * capabilities of the AP station, and choose the watermark accordingly. 4317303628Ssbruno */ 4318303628Ssbruno if (ni) { 4319303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) { 4320303628Ssbruno#ifdef notyet 4321303628Ssbruno if (ni->ni_rxmcs[2] != 0) 4322303628Ssbruno watermark = IWM_SF_W_MARK_MIMO3; 4323303628Ssbruno else if (ni->ni_rxmcs[1] != 0) 4324303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4325303628Ssbruno else 4326303628Ssbruno#endif 4327303628Ssbruno watermark = IWM_SF_W_MARK_SISO; 4328303628Ssbruno } else { 4329303628Ssbruno watermark = IWM_SF_W_MARK_LEGACY; 4330286441Srpaulo } 4331303628Ssbruno /* default watermark value for unassociated mode. */ 4332286441Srpaulo } else { 4333303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4334286441Srpaulo } 4335303628Ssbruno sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark); 4336286441Srpaulo 4337303628Ssbruno for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) { 4338303628Ssbruno for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) { 4339303628Ssbruno sf_cmd->long_delay_timeouts[i][j] = 4340303628Ssbruno htole32(IWM_SF_LONG_DELAY_AGING_TIMER); 4341303628Ssbruno } 4342286441Srpaulo } 4343303628Ssbruno 4344303628Ssbruno if (ni) { 4345303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout, 4346303628Ssbruno sizeof(iwm_sf_full_timeout)); 4347303628Ssbruno } else { 4348303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def, 4349303628Ssbruno sizeof(iwm_sf_full_timeout_def)); 4350303628Ssbruno } 4351286441Srpaulo} 4352286441Srpaulo 4353286441Srpaulostatic int 4354303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) 4355303628Ssbruno{ 4356303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 4357303628Ssbruno struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4358303628Ssbruno struct iwm_sf_cfg_cmd sf_cmd = { 4359303628Ssbruno .state = htole32(IWM_SF_FULL_ON), 4360303628Ssbruno }; 4361303628Ssbruno int ret = 0; 4362303628Ssbruno 4363303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) 4364303628Ssbruno sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF); 4365303628Ssbruno 4366303628Ssbruno switch (new_state) { 4367303628Ssbruno case IWM_SF_UNINIT: 4368303628Ssbruno case IWM_SF_INIT_OFF: 4369303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL); 4370303628Ssbruno break; 4371303628Ssbruno case IWM_SF_FULL_ON: 4372303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss); 4373303628Ssbruno break; 4374303628Ssbruno default: 4375303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE, 4376303628Ssbruno "Invalid state: %d. not sending Smart Fifo cmd\n", 4377303628Ssbruno new_state); 4378303628Ssbruno return EINVAL; 4379303628Ssbruno } 4380303628Ssbruno 4381303628Ssbruno ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC, 4382303628Ssbruno sizeof(sf_cmd), &sf_cmd); 4383303628Ssbruno return ret; 4384303628Ssbruno} 4385303628Ssbruno 4386303628Ssbrunostatic int 4387303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc) 4388303628Ssbruno{ 4389303628Ssbruno struct iwm_bt_coex_cmd bt_cmd; 4390303628Ssbruno 4391303628Ssbruno bt_cmd.mode = htole32(IWM_BT_COEX_WIFI); 4392303628Ssbruno bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET); 4393303628Ssbruno 4394303628Ssbruno return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd), 4395303628Ssbruno &bt_cmd); 4396303628Ssbruno} 4397303628Ssbruno 4398303628Ssbrunostatic int 4399303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) 4400303628Ssbruno{ 4401303628Ssbruno struct iwm_mcc_update_cmd mcc_cmd; 4402303628Ssbruno struct iwm_host_cmd hcmd = { 4403303628Ssbruno .id = IWM_MCC_UPDATE_CMD, 4404303628Ssbruno .flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB), 4405303628Ssbruno .data = { &mcc_cmd }, 4406303628Ssbruno }; 4407303628Ssbruno int ret; 4408303628Ssbruno#ifdef IWM_DEBUG 4409303628Ssbruno struct iwm_rx_packet *pkt; 4410303628Ssbruno struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL; 4411303628Ssbruno struct iwm_mcc_update_resp *mcc_resp; 4412303628Ssbruno int n_channels; 4413303628Ssbruno uint16_t mcc; 4414303628Ssbruno#endif 4415303628Ssbruno int resp_v2 = isset(sc->sc_enabled_capa, 4416303628Ssbruno IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); 4417303628Ssbruno 4418303628Ssbruno memset(&mcc_cmd, 0, sizeof(mcc_cmd)); 4419303628Ssbruno mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]); 4420303628Ssbruno if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || 4421303628Ssbruno isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC)) 4422303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT; 4423303628Ssbruno else 4424303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW; 4425303628Ssbruno 4426303628Ssbruno if (resp_v2) 4427303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd); 4428303628Ssbruno else 4429303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1); 4430303628Ssbruno 4431303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_NODE, 4432303628Ssbruno "send MCC update to FW with '%c%c' src = %d\n", 4433303628Ssbruno alpha2[0], alpha2[1], mcc_cmd.source_id); 4434303628Ssbruno 4435303628Ssbruno ret = iwm_send_cmd(sc, &hcmd); 4436303628Ssbruno if (ret) 4437303628Ssbruno return ret; 4438303628Ssbruno 4439303628Ssbruno#ifdef IWM_DEBUG 4440303628Ssbruno pkt = hcmd.resp_pkt; 4441303628Ssbruno 4442303628Ssbruno /* Extract MCC response */ 4443303628Ssbruno if (resp_v2) { 4444303628Ssbruno mcc_resp = (void *)pkt->data; 4445303628Ssbruno mcc = mcc_resp->mcc; 4446303628Ssbruno n_channels = le32toh(mcc_resp->n_channels); 4447303628Ssbruno } else { 4448303628Ssbruno mcc_resp_v1 = (void *)pkt->data; 4449303628Ssbruno mcc = mcc_resp_v1->mcc; 4450303628Ssbruno n_channels = le32toh(mcc_resp_v1->n_channels); 4451303628Ssbruno } 4452303628Ssbruno 4453303628Ssbruno /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ 4454303628Ssbruno if (mcc == 0) 4455303628Ssbruno mcc = 0x3030; /* "00" - world */ 4456303628Ssbruno 4457303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_NODE, 4458303628Ssbruno "regulatory domain '%c%c' (%d channels available)\n", 4459303628Ssbruno mcc >> 8, mcc & 0xff, n_channels); 4460303628Ssbruno#endif 4461303628Ssbruno iwm_free_resp(sc, &hcmd); 4462303628Ssbruno 4463303628Ssbruno return 0; 4464303628Ssbruno} 4465303628Ssbruno 4466303628Ssbrunostatic void 4467303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff) 4468303628Ssbruno{ 4469303628Ssbruno struct iwm_host_cmd cmd = { 4470303628Ssbruno .id = IWM_REPLY_THERMAL_MNG_BACKOFF, 4471303628Ssbruno .len = { sizeof(uint32_t), }, 4472303628Ssbruno .data = { &backoff, }, 4473303628Ssbruno }; 4474303628Ssbruno 4475303628Ssbruno if (iwm_send_cmd(sc, &cmd) != 0) { 4476303628Ssbruno device_printf(sc->sc_dev, 4477303628Ssbruno "failed to change thermal tx backoff\n"); 4478303628Ssbruno } 4479303628Ssbruno} 4480303628Ssbruno 4481303628Ssbrunostatic int 4482286441Srpauloiwm_init_hw(struct iwm_softc *sc) 4483286441Srpaulo{ 4484287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4485303628Ssbruno int error, i, ac; 4486286441Srpaulo 4487303628Ssbruno if ((error = iwm_start_hw(sc)) != 0) { 4488303628Ssbruno printf("iwm_start_hw: failed %d\n", error); 4489286441Srpaulo return error; 4490303628Ssbruno } 4491286441Srpaulo 4492286441Srpaulo if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) { 4493303628Ssbruno printf("iwm_run_init_mvm_ucode: failed %d\n", error); 4494286441Srpaulo return error; 4495286441Srpaulo } 4496286441Srpaulo 4497286441Srpaulo /* 4498286441Srpaulo * should stop and start HW since that INIT 4499286441Srpaulo * image just loaded 4500286441Srpaulo */ 4501286441Srpaulo iwm_stop_device(sc); 4502286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 4503286441Srpaulo device_printf(sc->sc_dev, "could not initialize hardware\n"); 4504286441Srpaulo return error; 4505286441Srpaulo } 4506286441Srpaulo 4507286441Srpaulo /* omstart, this time with the regular firmware */ 4508286441Srpaulo error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR); 4509286441Srpaulo if (error) { 4510286441Srpaulo device_printf(sc->sc_dev, "could not load firmware\n"); 4511286441Srpaulo goto error; 4512286441Srpaulo } 4513286441Srpaulo 4514303628Ssbruno if ((error = iwm_send_bt_init_conf(sc)) != 0) { 4515303628Ssbruno device_printf(sc->sc_dev, "bt init conf failed\n"); 4516286441Srpaulo goto error; 4517303628Ssbruno } 4518286441Srpaulo 4519303628Ssbruno if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { 4520303628Ssbruno device_printf(sc->sc_dev, "antenna config failed\n"); 4521303628Ssbruno goto error; 4522303628Ssbruno } 4523303628Ssbruno 4524286441Srpaulo /* Send phy db control command and then phy db calibration*/ 4525303628Ssbruno if ((error = iwm_send_phy_db_data(sc)) != 0) { 4526303628Ssbruno device_printf(sc->sc_dev, "phy_db_data failed\n"); 4527286441Srpaulo goto error; 4528303628Ssbruno } 4529286441Srpaulo 4530303628Ssbruno if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) { 4531303628Ssbruno device_printf(sc->sc_dev, "phy_cfg_cmd failed\n"); 4532286441Srpaulo goto error; 4533303628Ssbruno } 4534286441Srpaulo 4535286441Srpaulo /* Add auxiliary station for scanning */ 4536303628Ssbruno if ((error = iwm_mvm_add_aux_sta(sc)) != 0) { 4537303628Ssbruno device_printf(sc->sc_dev, "add_aux_sta failed\n"); 4538286441Srpaulo goto error; 4539303628Ssbruno } 4540286441Srpaulo 4541286441Srpaulo for (i = 0; i < IWM_NUM_PHY_CTX; i++) { 4542286441Srpaulo /* 4543286441Srpaulo * The channel used here isn't relevant as it's 4544286441Srpaulo * going to be overwritten in the other flows. 4545286441Srpaulo * For now use the first channel we have. 4546286441Srpaulo */ 4547286441Srpaulo if ((error = iwm_mvm_phy_ctxt_add(sc, 4548286441Srpaulo &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0) 4549286441Srpaulo goto error; 4550286441Srpaulo } 4551286441Srpaulo 4552303628Ssbruno /* Initialize tx backoffs to the minimum. */ 4553303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_7000) 4554303628Ssbruno iwm_mvm_tt_tx_backoff(sc, 0); 4555303628Ssbruno 4556286441Srpaulo error = iwm_mvm_power_update_device(sc); 4557286441Srpaulo if (error) 4558286441Srpaulo goto error; 4559286441Srpaulo 4560303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) { 4561303628Ssbruno if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) 4562303628Ssbruno goto error; 4563286441Srpaulo } 4564286441Srpaulo 4565303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { 4566303628Ssbruno if ((error = iwm_mvm_config_umac_scan(sc)) != 0) 4567303628Ssbruno goto error; 4568303628Ssbruno } 4569303628Ssbruno 4570303628Ssbruno /* Enable Tx queues. */ 4571303628Ssbruno for (ac = 0; ac < WME_NUM_AC; ac++) { 4572303628Ssbruno error = iwm_enable_txq(sc, IWM_STATION_ID, ac, 4573303628Ssbruno iwm_mvm_ac_to_tx_fifo[ac]); 4574303628Ssbruno if (error) 4575303628Ssbruno goto error; 4576303628Ssbruno } 4577303628Ssbruno 4578303628Ssbruno if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) { 4579303628Ssbruno device_printf(sc->sc_dev, "failed to disable beacon filter\n"); 4580303628Ssbruno goto error; 4581303628Ssbruno } 4582303628Ssbruno 4583286441Srpaulo return 0; 4584286441Srpaulo 4585286441Srpaulo error: 4586286441Srpaulo iwm_stop_device(sc); 4587286441Srpaulo return error; 4588286441Srpaulo} 4589286441Srpaulo 4590286441Srpaulo/* Allow multicast from our BSSID. */ 4591286441Srpaulostatic int 4592286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc) 4593286441Srpaulo{ 4594286441Srpaulo struct ieee80211_node *ni = vap->iv_bss; 4595286441Srpaulo struct iwm_mcast_filter_cmd *cmd; 4596286441Srpaulo size_t size; 4597286441Srpaulo int error; 4598286441Srpaulo 4599286441Srpaulo size = roundup(sizeof(*cmd), 4); 4600286441Srpaulo cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); 4601286441Srpaulo if (cmd == NULL) 4602286441Srpaulo return ENOMEM; 4603286441Srpaulo cmd->filter_own = 1; 4604286441Srpaulo cmd->port_id = 0; 4605286441Srpaulo cmd->count = 0; 4606286441Srpaulo cmd->pass_all = 1; 4607286441Srpaulo IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); 4608286441Srpaulo 4609286441Srpaulo error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 4610286441Srpaulo IWM_CMD_SYNC, size, cmd); 4611286441Srpaulo free(cmd, M_DEVBUF); 4612286441Srpaulo 4613286441Srpaulo return (error); 4614286441Srpaulo} 4615286441Srpaulo 4616303628Ssbruno/* 4617303628Ssbruno * ifnet interfaces 4618303628Ssbruno */ 4619303628Ssbruno 4620286441Srpaulostatic void 4621287197Sglebiusiwm_init(struct iwm_softc *sc) 4622286441Srpaulo{ 4623286441Srpaulo int error; 4624286441Srpaulo 4625286441Srpaulo if (sc->sc_flags & IWM_FLAG_HW_INITED) { 4626286441Srpaulo return; 4627286441Srpaulo } 4628286441Srpaulo sc->sc_generation++; 4629286441Srpaulo sc->sc_flags &= ~IWM_FLAG_STOPPED; 4630286441Srpaulo 4631286441Srpaulo if ((error = iwm_init_hw(sc)) != 0) { 4632303628Ssbruno printf("iwm_init_hw failed %d\n", error); 4633287197Sglebius iwm_stop(sc); 4634286441Srpaulo return; 4635286441Srpaulo } 4636286441Srpaulo 4637286441Srpaulo /* 4638303628Ssbruno * Ok, firmware loaded and we are jogging 4639286441Srpaulo */ 4640286441Srpaulo sc->sc_flags |= IWM_FLAG_HW_INITED; 4641286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 4642286441Srpaulo} 4643286441Srpaulo 4644287197Sglebiusstatic int 4645287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m) 4646286441Srpaulo{ 4647287197Sglebius struct iwm_softc *sc; 4648287197Sglebius int error; 4649286441Srpaulo 4650287197Sglebius sc = ic->ic_softc; 4651287197Sglebius 4652286441Srpaulo IWM_LOCK(sc); 4653287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 4654287197Sglebius IWM_UNLOCK(sc); 4655287197Sglebius return (ENXIO); 4656287197Sglebius } 4657287197Sglebius error = mbufq_enqueue(&sc->sc_snd, m); 4658287197Sglebius if (error) { 4659287197Sglebius IWM_UNLOCK(sc); 4660287197Sglebius return (error); 4661287197Sglebius } 4662287197Sglebius iwm_start(sc); 4663286441Srpaulo IWM_UNLOCK(sc); 4664287197Sglebius return (0); 4665286441Srpaulo} 4666286441Srpaulo 4667287197Sglebius/* 4668287197Sglebius * Dequeue packets from sendq and call send. 4669287197Sglebius */ 4670286441Srpaulostatic void 4671287197Sglebiusiwm_start(struct iwm_softc *sc) 4672286441Srpaulo{ 4673286441Srpaulo struct ieee80211_node *ni; 4674286441Srpaulo struct mbuf *m; 4675286441Srpaulo int ac = 0; 4676286441Srpaulo 4677286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__); 4678287197Sglebius while (sc->qfullmsk == 0 && 4679287197Sglebius (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 4680286441Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 4681286441Srpaulo if (iwm_tx(sc, m, ni, ac) != 0) { 4682287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 4683287197Sglebius IFCOUNTER_OERRORS, 1); 4684286441Srpaulo ieee80211_free_node(ni); 4685286441Srpaulo continue; 4686286441Srpaulo } 4687287197Sglebius sc->sc_tx_timer = 15; 4688286441Srpaulo } 4689286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); 4690286441Srpaulo} 4691286441Srpaulo 4692286441Srpaulostatic void 4693287197Sglebiusiwm_stop(struct iwm_softc *sc) 4694286441Srpaulo{ 4695286441Srpaulo 4696286441Srpaulo sc->sc_flags &= ~IWM_FLAG_HW_INITED; 4697286441Srpaulo sc->sc_flags |= IWM_FLAG_STOPPED; 4698286441Srpaulo sc->sc_generation++; 4699301187Sadrian iwm_led_blink_stop(sc); 4700286441Srpaulo sc->sc_tx_timer = 0; 4701286441Srpaulo iwm_stop_device(sc); 4702286441Srpaulo} 4703286441Srpaulo 4704286441Srpaulostatic void 4705286441Srpauloiwm_watchdog(void *arg) 4706286441Srpaulo{ 4707286441Srpaulo struct iwm_softc *sc = arg; 4708300242Savos struct ieee80211com *ic = &sc->sc_ic; 4709286441Srpaulo 4710286441Srpaulo if (sc->sc_tx_timer > 0) { 4711286441Srpaulo if (--sc->sc_tx_timer == 0) { 4712286441Srpaulo device_printf(sc->sc_dev, "device timeout\n"); 4713286441Srpaulo#ifdef IWM_DEBUG 4714286441Srpaulo iwm_nic_error(sc); 4715286441Srpaulo#endif 4716300242Savos ieee80211_restart_all(ic); 4717303628Ssbruno counter_u64_add(sc->sc_ic.ic_oerrors, 1); 4718286441Srpaulo return; 4719286441Srpaulo } 4720286441Srpaulo } 4721286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 4722286441Srpaulo} 4723286441Srpaulo 4724287197Sglebiusstatic void 4725287197Sglebiusiwm_parent(struct ieee80211com *ic) 4726286441Srpaulo{ 4727287197Sglebius struct iwm_softc *sc = ic->ic_softc; 4728287197Sglebius int startall = 0; 4729286441Srpaulo 4730287197Sglebius IWM_LOCK(sc); 4731287197Sglebius if (ic->ic_nrunning > 0) { 4732287197Sglebius if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) { 4733287197Sglebius iwm_init(sc); 4734287197Sglebius startall = 1; 4735286441Srpaulo } 4736287197Sglebius } else if (sc->sc_flags & IWM_FLAG_HW_INITED) 4737287197Sglebius iwm_stop(sc); 4738287197Sglebius IWM_UNLOCK(sc); 4739287197Sglebius if (startall) 4740287197Sglebius ieee80211_start_all(ic); 4741286441Srpaulo} 4742286441Srpaulo 4743286441Srpaulo/* 4744286441Srpaulo * The interrupt side of things 4745286441Srpaulo */ 4746286441Srpaulo 4747286441Srpaulo/* 4748286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c 4749286441Srpaulo */ 4750286441Srpaulo 4751286441Srpaulo/* 4752286441Srpaulo * Note: This structure is read from the device with IO accesses, 4753286441Srpaulo * and the reading already does the endian conversion. As it is 4754286441Srpaulo * read with uint32_t-sized accesses, any members with a different size 4755286441Srpaulo * need to be ordered correctly though! 4756286441Srpaulo */ 4757286441Srpaulostruct iwm_error_event_table { 4758286441Srpaulo uint32_t valid; /* (nonzero) valid, (0) log is empty */ 4759286441Srpaulo uint32_t error_id; /* type of error */ 4760303628Ssbruno uint32_t trm_hw_status0; /* TRM HW status */ 4761303628Ssbruno uint32_t trm_hw_status1; /* TRM HW status */ 4762286441Srpaulo uint32_t blink2; /* branch link */ 4763286441Srpaulo uint32_t ilink1; /* interrupt link */ 4764286441Srpaulo uint32_t ilink2; /* interrupt link */ 4765286441Srpaulo uint32_t data1; /* error-specific data */ 4766286441Srpaulo uint32_t data2; /* error-specific data */ 4767286441Srpaulo uint32_t data3; /* error-specific data */ 4768286441Srpaulo uint32_t bcon_time; /* beacon timer */ 4769286441Srpaulo uint32_t tsf_low; /* network timestamp function timer */ 4770286441Srpaulo uint32_t tsf_hi; /* network timestamp function timer */ 4771286441Srpaulo uint32_t gp1; /* GP1 timer register */ 4772286441Srpaulo uint32_t gp2; /* GP2 timer register */ 4773303628Ssbruno uint32_t fw_rev_type; /* firmware revision type */ 4774303628Ssbruno uint32_t major; /* uCode version major */ 4775303628Ssbruno uint32_t minor; /* uCode version minor */ 4776286441Srpaulo uint32_t hw_ver; /* HW Silicon version */ 4777286441Srpaulo uint32_t brd_ver; /* HW board version */ 4778286441Srpaulo uint32_t log_pc; /* log program counter */ 4779286441Srpaulo uint32_t frame_ptr; /* frame pointer */ 4780286441Srpaulo uint32_t stack_ptr; /* stack pointer */ 4781286441Srpaulo uint32_t hcmd; /* last host command header */ 4782286441Srpaulo uint32_t isr0; /* isr status register LMPM_NIC_ISR0: 4783286441Srpaulo * rxtx_flag */ 4784286441Srpaulo uint32_t isr1; /* isr status register LMPM_NIC_ISR1: 4785286441Srpaulo * host_flag */ 4786286441Srpaulo uint32_t isr2; /* isr status register LMPM_NIC_ISR2: 4787286441Srpaulo * enc_flag */ 4788286441Srpaulo uint32_t isr3; /* isr status register LMPM_NIC_ISR3: 4789286441Srpaulo * time_flag */ 4790286441Srpaulo uint32_t isr4; /* isr status register LMPM_NIC_ISR4: 4791286441Srpaulo * wico interrupt */ 4792303628Ssbruno uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ 4793286441Srpaulo uint32_t wait_event; /* wait event() caller address */ 4794286441Srpaulo uint32_t l2p_control; /* L2pControlField */ 4795286441Srpaulo uint32_t l2p_duration; /* L2pDurationField */ 4796286441Srpaulo uint32_t l2p_mhvalid; /* L2pMhValidBits */ 4797286441Srpaulo uint32_t l2p_addr_match; /* L2pAddrMatchStat */ 4798286441Srpaulo uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on 4799286441Srpaulo * (LMPM_PMG_SEL) */ 4800286441Srpaulo uint32_t u_timestamp; /* indicate when the date and time of the 4801286441Srpaulo * compilation */ 4802286441Srpaulo uint32_t flow_handler; /* FH read/write pointers, RX credit */ 4803303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; 4804303628Ssbruno 4805303628Ssbruno/* 4806303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip. 4807303628Ssbruno * Note: This structure is read from the device with IO accesses, 4808303628Ssbruno * and the reading already does the endian conversion. As it is 4809303628Ssbruno * read with u32-sized accesses, any members with a different size 4810303628Ssbruno * need to be ordered correctly though! 4811303628Ssbruno */ 4812303628Ssbrunostruct iwm_umac_error_event_table { 4813303628Ssbruno uint32_t valid; /* (nonzero) valid, (0) log is empty */ 4814303628Ssbruno uint32_t error_id; /* type of error */ 4815303628Ssbruno uint32_t blink1; /* branch link */ 4816303628Ssbruno uint32_t blink2; /* branch link */ 4817303628Ssbruno uint32_t ilink1; /* interrupt link */ 4818303628Ssbruno uint32_t ilink2; /* interrupt link */ 4819303628Ssbruno uint32_t data1; /* error-specific data */ 4820303628Ssbruno uint32_t data2; /* error-specific data */ 4821303628Ssbruno uint32_t data3; /* error-specific data */ 4822303628Ssbruno uint32_t umac_major; 4823303628Ssbruno uint32_t umac_minor; 4824303628Ssbruno uint32_t frame_pointer; /* core register 27*/ 4825303628Ssbruno uint32_t stack_pointer; /* core register 28 */ 4826303628Ssbruno uint32_t cmd_header; /* latest host cmd sent to UMAC */ 4827303628Ssbruno uint32_t nic_isr_pref; /* ISR status register */ 4828286441Srpaulo} __packed; 4829286441Srpaulo 4830286441Srpaulo#define ERROR_START_OFFSET (1 * sizeof(uint32_t)) 4831286441Srpaulo#define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) 4832286441Srpaulo 4833286441Srpaulo#ifdef IWM_DEBUG 4834286441Srpaulostruct { 4835286441Srpaulo const char *name; 4836286441Srpaulo uint8_t num; 4837286441Srpaulo} advanced_lookup[] = { 4838286441Srpaulo { "NMI_INTERRUPT_WDG", 0x34 }, 4839286441Srpaulo { "SYSASSERT", 0x35 }, 4840286441Srpaulo { "UCODE_VERSION_MISMATCH", 0x37 }, 4841286441Srpaulo { "BAD_COMMAND", 0x38 }, 4842286441Srpaulo { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, 4843286441Srpaulo { "FATAL_ERROR", 0x3D }, 4844286441Srpaulo { "NMI_TRM_HW_ERR", 0x46 }, 4845286441Srpaulo { "NMI_INTERRUPT_TRM", 0x4C }, 4846286441Srpaulo { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, 4847286441Srpaulo { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, 4848286441Srpaulo { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, 4849286441Srpaulo { "NMI_INTERRUPT_HOST", 0x66 }, 4850286441Srpaulo { "NMI_INTERRUPT_ACTION_PT", 0x7C }, 4851286441Srpaulo { "NMI_INTERRUPT_UNKNOWN", 0x84 }, 4852286441Srpaulo { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, 4853286441Srpaulo { "ADVANCED_SYSASSERT", 0 }, 4854286441Srpaulo}; 4855286441Srpaulo 4856286441Srpaulostatic const char * 4857286441Srpauloiwm_desc_lookup(uint32_t num) 4858286441Srpaulo{ 4859286441Srpaulo int i; 4860286441Srpaulo 4861286441Srpaulo for (i = 0; i < nitems(advanced_lookup) - 1; i++) 4862286441Srpaulo if (advanced_lookup[i].num == num) 4863286441Srpaulo return advanced_lookup[i].name; 4864286441Srpaulo 4865286441Srpaulo /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ 4866286441Srpaulo return advanced_lookup[i].name; 4867286441Srpaulo} 4868286441Srpaulo 4869303628Ssbrunostatic void 4870303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc) 4871303628Ssbruno{ 4872303628Ssbruno struct iwm_umac_error_event_table table; 4873303628Ssbruno uint32_t base; 4874303628Ssbruno 4875303628Ssbruno base = sc->sc_uc.uc_umac_error_event_table; 4876303628Ssbruno 4877303628Ssbruno if (base < 0x800000) { 4878303628Ssbruno device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", 4879303628Ssbruno base); 4880303628Ssbruno return; 4881303628Ssbruno } 4882303628Ssbruno 4883303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 4884303628Ssbruno device_printf(sc->sc_dev, "reading errlog failed\n"); 4885303628Ssbruno return; 4886303628Ssbruno } 4887303628Ssbruno 4888303628Ssbruno if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 4889303628Ssbruno device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n"); 4890303628Ssbruno device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 4891303628Ssbruno sc->sc_flags, table.valid); 4892303628Ssbruno } 4893303628Ssbruno 4894303628Ssbruno device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id, 4895303628Ssbruno iwm_desc_lookup(table.error_id)); 4896303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1); 4897303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2); 4898303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n", 4899303628Ssbruno table.ilink1); 4900303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n", 4901303628Ssbruno table.ilink2); 4902303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1); 4903303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2); 4904303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3); 4905303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major); 4906303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor); 4907303628Ssbruno device_printf(sc->sc_dev, "0x%08X | frame pointer\n", 4908303628Ssbruno table.frame_pointer); 4909303628Ssbruno device_printf(sc->sc_dev, "0x%08X | stack pointer\n", 4910303628Ssbruno table.stack_pointer); 4911303628Ssbruno device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header); 4912303628Ssbruno device_printf(sc->sc_dev, "0x%08X | isr status reg\n", 4913303628Ssbruno table.nic_isr_pref); 4914303628Ssbruno} 4915303628Ssbruno 4916286441Srpaulo/* 4917286441Srpaulo * Support for dumping the error log seemed like a good idea ... 4918286441Srpaulo * but it's mostly hex junk and the only sensible thing is the 4919286441Srpaulo * hw/ucode revision (which we know anyway). Since it's here, 4920286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to 4921286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later. 4922286441Srpaulo */ 4923286441Srpaulostatic void 4924286441Srpauloiwm_nic_error(struct iwm_softc *sc) 4925286441Srpaulo{ 4926286441Srpaulo struct iwm_error_event_table table; 4927286441Srpaulo uint32_t base; 4928286441Srpaulo 4929286441Srpaulo device_printf(sc->sc_dev, "dumping device error log\n"); 4930286441Srpaulo base = sc->sc_uc.uc_error_event_table; 4931303628Ssbruno if (base < 0x800000) { 4932286441Srpaulo device_printf(sc->sc_dev, 4933303628Ssbruno "Invalid error log pointer 0x%08x\n", base); 4934286441Srpaulo return; 4935286441Srpaulo } 4936286441Srpaulo 4937303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 4938286441Srpaulo device_printf(sc->sc_dev, "reading errlog failed\n"); 4939286441Srpaulo return; 4940286441Srpaulo } 4941286441Srpaulo 4942286441Srpaulo if (!table.valid) { 4943286441Srpaulo device_printf(sc->sc_dev, "errlog not found, skipping\n"); 4944286441Srpaulo return; 4945286441Srpaulo } 4946286441Srpaulo 4947286441Srpaulo if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 4948303628Ssbruno device_printf(sc->sc_dev, "Start Error Log Dump:\n"); 4949286441Srpaulo device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 4950286441Srpaulo sc->sc_flags, table.valid); 4951286441Srpaulo } 4952286441Srpaulo 4953286441Srpaulo device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id, 4954303628Ssbruno iwm_desc_lookup(table.error_id)); 4955303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status0\n", 4956303628Ssbruno table.trm_hw_status0); 4957303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status1\n", 4958303628Ssbruno table.trm_hw_status1); 4959286441Srpaulo device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2); 4960286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1); 4961286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2); 4962286441Srpaulo device_printf(sc->sc_dev, "%08X | data1\n", table.data1); 4963286441Srpaulo device_printf(sc->sc_dev, "%08X | data2\n", table.data2); 4964286441Srpaulo device_printf(sc->sc_dev, "%08X | data3\n", table.data3); 4965286441Srpaulo device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time); 4966286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low); 4967286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi); 4968286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1); 4969286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2); 4970303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode revision type\n", 4971303628Ssbruno table.fw_rev_type); 4972303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major); 4973303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor); 4974286441Srpaulo device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver); 4975286441Srpaulo device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver); 4976286441Srpaulo device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd); 4977286441Srpaulo device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0); 4978286441Srpaulo device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1); 4979286441Srpaulo device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2); 4980286441Srpaulo device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3); 4981286441Srpaulo device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4); 4982303628Ssbruno device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id); 4983286441Srpaulo device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event); 4984286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control); 4985286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration); 4986286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid); 4987286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match); 4988286441Srpaulo device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); 4989286441Srpaulo device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); 4990286441Srpaulo device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); 4991303628Ssbruno 4992303628Ssbruno if (sc->sc_uc.uc_umac_error_event_table) 4993303628Ssbruno iwm_nic_umac_error(sc); 4994286441Srpaulo} 4995286441Srpaulo#endif 4996286441Srpaulo 4997286441Srpaulo#define SYNC_RESP_STRUCT(_var_, _pkt_) \ 4998286441Srpaulodo { \ 4999286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ 5000286441Srpaulo _var_ = (void *)((_pkt_)+1); \ 5001286441Srpaulo} while (/*CONSTCOND*/0) 5002286441Srpaulo 5003286441Srpaulo#define SYNC_RESP_PTR(_ptr_, _len_, _pkt_) \ 5004286441Srpaulodo { \ 5005286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ 5006286441Srpaulo _ptr_ = (void *)((_pkt_)+1); \ 5007286441Srpaulo} while (/*CONSTCOND*/0) 5008286441Srpaulo 5009286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); 5010286441Srpaulo 5011286441Srpaulo/* 5012286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt. 5013286441Srpaulo * Basic structure from if_iwn 5014286441Srpaulo */ 5015286441Srpaulostatic void 5016286441Srpauloiwm_notif_intr(struct iwm_softc *sc) 5017286441Srpaulo{ 5018303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 5019286441Srpaulo uint16_t hw; 5020286441Srpaulo 5021286441Srpaulo bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 5022286441Srpaulo BUS_DMASYNC_POSTREAD); 5023286441Srpaulo 5024286441Srpaulo hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; 5025303628Ssbruno 5026303628Ssbruno /* 5027303628Ssbruno * Process responses 5028303628Ssbruno */ 5029286441Srpaulo while (sc->rxq.cur != hw) { 5030286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 5031286441Srpaulo struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 5032286441Srpaulo struct iwm_rx_packet *pkt; 5033286441Srpaulo struct iwm_cmd_response *cresp; 5034303628Ssbruno int qid, idx, code; 5035286441Srpaulo 5036286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 5037286441Srpaulo BUS_DMASYNC_POSTREAD); 5038286441Srpaulo pkt = mtod(data->m, struct iwm_rx_packet *); 5039286441Srpaulo 5040286441Srpaulo qid = pkt->hdr.qid & ~0x80; 5041286441Srpaulo idx = pkt->hdr.idx; 5042286441Srpaulo 5043303628Ssbruno code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); 5044286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5045303628Ssbruno "rx packet qid=%d idx=%d type=%x %d %d\n", 5046303628Ssbruno pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, sc->rxq.cur, hw); 5047286441Srpaulo 5048286441Srpaulo /* 5049286441Srpaulo * randomly get these from the firmware, no idea why. 5050286441Srpaulo * they at least seem harmless, so just ignore them for now 5051286441Srpaulo */ 5052286441Srpaulo if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) 5053286441Srpaulo || pkt->len_n_flags == htole32(0x55550000))) { 5054286441Srpaulo ADVANCE_RXQ(sc); 5055286441Srpaulo continue; 5056286441Srpaulo } 5057286441Srpaulo 5058303628Ssbruno switch (code) { 5059286441Srpaulo case IWM_REPLY_RX_PHY_CMD: 5060286441Srpaulo iwm_mvm_rx_rx_phy_cmd(sc, pkt, data); 5061286441Srpaulo break; 5062286441Srpaulo 5063286441Srpaulo case IWM_REPLY_RX_MPDU_CMD: 5064286441Srpaulo iwm_mvm_rx_rx_mpdu(sc, pkt, data); 5065286441Srpaulo break; 5066286441Srpaulo 5067286441Srpaulo case IWM_TX_CMD: 5068286441Srpaulo iwm_mvm_rx_tx_cmd(sc, pkt, data); 5069286441Srpaulo break; 5070286441Srpaulo 5071286441Srpaulo case IWM_MISSED_BEACONS_NOTIFICATION: { 5072286441Srpaulo struct iwm_missed_beacons_notif *resp; 5073286441Srpaulo int missed; 5074286441Srpaulo 5075286441Srpaulo /* XXX look at mac_id to determine interface ID */ 5076286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5077286441Srpaulo 5078286441Srpaulo SYNC_RESP_STRUCT(resp, pkt); 5079286441Srpaulo missed = le32toh(resp->consec_missed_beacons); 5080286441Srpaulo 5081286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE, 5082286441Srpaulo "%s: MISSED_BEACON: mac_id=%d, " 5083286441Srpaulo "consec_since_last_rx=%d, consec=%d, num_expect=%d " 5084286441Srpaulo "num_rx=%d\n", 5085286441Srpaulo __func__, 5086286441Srpaulo le32toh(resp->mac_id), 5087286441Srpaulo le32toh(resp->consec_missed_beacons_since_last_rx), 5088286441Srpaulo le32toh(resp->consec_missed_beacons), 5089286441Srpaulo le32toh(resp->num_expected_beacons), 5090286441Srpaulo le32toh(resp->num_recvd_beacons)); 5091286441Srpaulo 5092286441Srpaulo /* Be paranoid */ 5093286441Srpaulo if (vap == NULL) 5094286441Srpaulo break; 5095286441Srpaulo 5096286441Srpaulo /* XXX no net80211 locking? */ 5097286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && 5098286441Srpaulo (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 5099286441Srpaulo if (missed > vap->iv_bmissthreshold) { 5100286441Srpaulo /* XXX bad locking; turn into task */ 5101286441Srpaulo IWM_UNLOCK(sc); 5102286441Srpaulo ieee80211_beacon_miss(ic); 5103286441Srpaulo IWM_LOCK(sc); 5104286441Srpaulo } 5105286441Srpaulo } 5106286441Srpaulo 5107286441Srpaulo break; } 5108286441Srpaulo 5109303628Ssbruno case IWM_MFUART_LOAD_NOTIFICATION: 5110303628Ssbruno break; 5111303628Ssbruno 5112286441Srpaulo case IWM_MVM_ALIVE: { 5113303628Ssbruno struct iwm_mvm_alive_resp_v1 *resp1; 5114303628Ssbruno struct iwm_mvm_alive_resp_v2 *resp2; 5115303628Ssbruno struct iwm_mvm_alive_resp_v3 *resp3; 5116286441Srpaulo 5117303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { 5118303628Ssbruno SYNC_RESP_STRUCT(resp1, pkt); 5119303628Ssbruno sc->sc_uc.uc_error_event_table 5120303628Ssbruno = le32toh(resp1->error_event_table_ptr); 5121303628Ssbruno sc->sc_uc.uc_log_event_table 5122303628Ssbruno = le32toh(resp1->log_event_table_ptr); 5123303628Ssbruno sc->sched_base = le32toh(resp1->scd_base_ptr); 5124303628Ssbruno if (resp1->status == IWM_ALIVE_STATUS_OK) 5125303628Ssbruno sc->sc_uc.uc_ok = 1; 5126303628Ssbruno else 5127303628Ssbruno sc->sc_uc.uc_ok = 0; 5128303628Ssbruno } 5129286441Srpaulo 5130303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { 5131303628Ssbruno SYNC_RESP_STRUCT(resp2, pkt); 5132303628Ssbruno sc->sc_uc.uc_error_event_table 5133303628Ssbruno = le32toh(resp2->error_event_table_ptr); 5134303628Ssbruno sc->sc_uc.uc_log_event_table 5135303628Ssbruno = le32toh(resp2->log_event_table_ptr); 5136303628Ssbruno sc->sched_base = le32toh(resp2->scd_base_ptr); 5137303628Ssbruno sc->sc_uc.uc_umac_error_event_table 5138303628Ssbruno = le32toh(resp2->error_info_addr); 5139303628Ssbruno if (resp2->status == IWM_ALIVE_STATUS_OK) 5140303628Ssbruno sc->sc_uc.uc_ok = 1; 5141303628Ssbruno else 5142303628Ssbruno sc->sc_uc.uc_ok = 0; 5143303628Ssbruno } 5144303628Ssbruno 5145303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { 5146303628Ssbruno SYNC_RESP_STRUCT(resp3, pkt); 5147303628Ssbruno sc->sc_uc.uc_error_event_table 5148303628Ssbruno = le32toh(resp3->error_event_table_ptr); 5149303628Ssbruno sc->sc_uc.uc_log_event_table 5150303628Ssbruno = le32toh(resp3->log_event_table_ptr); 5151303628Ssbruno sc->sched_base = le32toh(resp3->scd_base_ptr); 5152303628Ssbruno sc->sc_uc.uc_umac_error_event_table 5153303628Ssbruno = le32toh(resp3->error_info_addr); 5154303628Ssbruno if (resp3->status == IWM_ALIVE_STATUS_OK) 5155303628Ssbruno sc->sc_uc.uc_ok = 1; 5156303628Ssbruno else 5157303628Ssbruno sc->sc_uc.uc_ok = 0; 5158303628Ssbruno } 5159303628Ssbruno 5160286441Srpaulo sc->sc_uc.uc_intr = 1; 5161286441Srpaulo wakeup(&sc->sc_uc); 5162286441Srpaulo break; } 5163286441Srpaulo 5164286441Srpaulo case IWM_CALIB_RES_NOTIF_PHY_DB: { 5165286441Srpaulo struct iwm_calib_res_notif_phy_db *phy_db_notif; 5166286441Srpaulo SYNC_RESP_STRUCT(phy_db_notif, pkt); 5167286441Srpaulo 5168286441Srpaulo iwm_phy_db_set_section(sc, phy_db_notif); 5169286441Srpaulo 5170286441Srpaulo break; } 5171286441Srpaulo 5172286441Srpaulo case IWM_STATISTICS_NOTIFICATION: { 5173286441Srpaulo struct iwm_notif_statistics *stats; 5174286441Srpaulo SYNC_RESP_STRUCT(stats, pkt); 5175286441Srpaulo memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); 5176330144Seadler sc->sc_noise = iwm_get_noise(sc, &stats->rx.general); 5177286441Srpaulo break; } 5178286441Srpaulo 5179286441Srpaulo case IWM_NVM_ACCESS_CMD: 5180303628Ssbruno case IWM_MCC_UPDATE_CMD: 5181286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 5182286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 5183286441Srpaulo BUS_DMASYNC_POSTREAD); 5184286441Srpaulo memcpy(sc->sc_cmd_resp, 5185286441Srpaulo pkt, sizeof(sc->sc_cmd_resp)); 5186286441Srpaulo } 5187286441Srpaulo break; 5188286441Srpaulo 5189303628Ssbruno case IWM_MCC_CHUB_UPDATE_CMD: { 5190303628Ssbruno struct iwm_mcc_chub_notif *notif; 5191303628Ssbruno SYNC_RESP_STRUCT(notif, pkt); 5192303628Ssbruno 5193303628Ssbruno sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; 5194303628Ssbruno sc->sc_fw_mcc[1] = notif->mcc & 0xff; 5195303628Ssbruno sc->sc_fw_mcc[2] = '\0'; 5196303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 5197303628Ssbruno "fw source %d sent CC '%s'\n", 5198303628Ssbruno notif->source_id, sc->sc_fw_mcc); 5199303628Ssbruno break; } 5200303628Ssbruno 5201303628Ssbruno case IWM_DTS_MEASUREMENT_NOTIFICATION: 5202303628Ssbruno break; 5203303628Ssbruno 5204286441Srpaulo case IWM_PHY_CONFIGURATION_CMD: 5205286441Srpaulo case IWM_TX_ANT_CONFIGURATION_CMD: 5206286441Srpaulo case IWM_ADD_STA: 5207286441Srpaulo case IWM_MAC_CONTEXT_CMD: 5208286441Srpaulo case IWM_REPLY_SF_CFG_CMD: 5209286441Srpaulo case IWM_POWER_TABLE_CMD: 5210286441Srpaulo case IWM_PHY_CONTEXT_CMD: 5211286441Srpaulo case IWM_BINDING_CONTEXT_CMD: 5212286441Srpaulo case IWM_TIME_EVENT_CMD: 5213303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): 5214303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): 5215303628Ssbruno case IWM_SCAN_OFFLOAD_REQUEST_CMD: 5216286441Srpaulo case IWM_REPLY_BEACON_FILTERING_CMD: 5217286441Srpaulo case IWM_MAC_PM_POWER_TABLE: 5218286441Srpaulo case IWM_TIME_QUOTA_CMD: 5219286441Srpaulo case IWM_REMOVE_STA: 5220286441Srpaulo case IWM_TXPATH_FLUSH: 5221286441Srpaulo case IWM_LQ_CMD: 5222303628Ssbruno case IWM_BT_CONFIG: 5223303628Ssbruno case IWM_REPLY_THERMAL_MNG_BACKOFF: 5224286441Srpaulo SYNC_RESP_STRUCT(cresp, pkt); 5225286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 5226286441Srpaulo memcpy(sc->sc_cmd_resp, 5227286441Srpaulo pkt, sizeof(*pkt)+sizeof(*cresp)); 5228286441Srpaulo } 5229286441Srpaulo break; 5230286441Srpaulo 5231286441Srpaulo /* ignore */ 5232286441Srpaulo case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */ 5233286441Srpaulo break; 5234286441Srpaulo 5235286441Srpaulo case IWM_INIT_COMPLETE_NOTIF: 5236286441Srpaulo sc->sc_init_complete = 1; 5237286441Srpaulo wakeup(&sc->sc_init_complete); 5238286441Srpaulo break; 5239286441Srpaulo 5240303628Ssbruno case IWM_SCAN_OFFLOAD_COMPLETE: { 5241303628Ssbruno struct iwm_periodic_scan_complete *notif; 5242286441Srpaulo SYNC_RESP_STRUCT(notif, pkt); 5243303628Ssbruno break; 5244303628Ssbruno } 5245303628Ssbruno 5246303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE: { 5247303628Ssbruno struct iwm_lmac_scan_complete_notif *notif; 5248303628Ssbruno SYNC_RESP_STRUCT(notif, pkt); 5249303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5250303628Ssbruno break; 5251303628Ssbruno } 5252303628Ssbruno 5253303628Ssbruno case IWM_SCAN_COMPLETE_UMAC: { 5254303628Ssbruno struct iwm_umac_scan_complete *notif; 5255303628Ssbruno SYNC_RESP_STRUCT(notif, pkt); 5256303628Ssbruno 5257303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, 5258303628Ssbruno "UMAC scan complete, status=0x%x\n", 5259303628Ssbruno notif->status); 5260303628Ssbruno#if 0 /* XXX This would be a duplicate scan end call */ 5261286441Srpaulo taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task); 5262303628Ssbruno#endif 5263303628Ssbruno break; 5264303628Ssbruno } 5265286441Srpaulo 5266303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE_UMAC: { 5267303628Ssbruno struct iwm_umac_scan_iter_complete_notif *notif; 5268303628Ssbruno SYNC_RESP_STRUCT(notif, pkt); 5269303628Ssbruno 5270303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration " 5271303628Ssbruno "complete, status=0x%x, %d channels scanned\n", 5272303628Ssbruno notif->status, notif->scanned_channels); 5273303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5274303628Ssbruno break; 5275303628Ssbruno } 5276303628Ssbruno 5277286441Srpaulo case IWM_REPLY_ERROR: { 5278286441Srpaulo struct iwm_error_resp *resp; 5279286441Srpaulo SYNC_RESP_STRUCT(resp, pkt); 5280286441Srpaulo 5281286441Srpaulo device_printf(sc->sc_dev, 5282286441Srpaulo "firmware error 0x%x, cmd 0x%x\n", 5283286441Srpaulo le32toh(resp->error_type), 5284286441Srpaulo resp->cmd_id); 5285303628Ssbruno break; 5286303628Ssbruno } 5287286441Srpaulo 5288286441Srpaulo case IWM_TIME_EVENT_NOTIFICATION: { 5289286441Srpaulo struct iwm_time_event_notif *notif; 5290286441Srpaulo SYNC_RESP_STRUCT(notif, pkt); 5291286441Srpaulo 5292286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5293300833Sadrian "TE notif status = 0x%x action = 0x%x\n", 5294303628Ssbruno notif->status, notif->action); 5295303628Ssbruno break; 5296303628Ssbruno } 5297286441Srpaulo 5298286441Srpaulo case IWM_MCAST_FILTER_CMD: 5299286441Srpaulo break; 5300286441Srpaulo 5301303628Ssbruno case IWM_SCD_QUEUE_CFG: { 5302303628Ssbruno struct iwm_scd_txq_cfg_rsp *rsp; 5303303628Ssbruno SYNC_RESP_STRUCT(rsp, pkt); 5304303628Ssbruno 5305303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_CMD, 5306303628Ssbruno "queue cfg token=0x%x sta_id=%d " 5307303628Ssbruno "tid=%d scd_queue=%d\n", 5308303628Ssbruno rsp->token, rsp->sta_id, rsp->tid, 5309303628Ssbruno rsp->scd_queue); 5310303628Ssbruno break; 5311303628Ssbruno } 5312303628Ssbruno 5313286441Srpaulo default: 5314286441Srpaulo device_printf(sc->sc_dev, 5315286441Srpaulo "frame %d/%d %x UNHANDLED (this should " 5316286441Srpaulo "not happen)\n", qid, idx, 5317286441Srpaulo pkt->len_n_flags); 5318286441Srpaulo break; 5319286441Srpaulo } 5320286441Srpaulo 5321286441Srpaulo /* 5322286441Srpaulo * Why test bit 0x80? The Linux driver: 5323286441Srpaulo * 5324286441Srpaulo * There is one exception: uCode sets bit 15 when it 5325286441Srpaulo * originates the response/notification, i.e. when the 5326286441Srpaulo * response/notification is not a direct response to a 5327286441Srpaulo * command sent by the driver. For example, uCode issues 5328286441Srpaulo * IWM_REPLY_RX when it sends a received frame to the driver; 5329286441Srpaulo * it is not a direct response to any driver command. 5330286441Srpaulo * 5331286441Srpaulo * Ok, so since when is 7 == 15? Well, the Linux driver 5332286441Srpaulo * uses a slightly different format for pkt->hdr, and "qid" 5333286441Srpaulo * is actually the upper byte of a two-byte field. 5334286441Srpaulo */ 5335286441Srpaulo if (!(pkt->hdr.qid & (1 << 7))) { 5336286441Srpaulo iwm_cmd_done(sc, pkt); 5337286441Srpaulo } 5338286441Srpaulo 5339286441Srpaulo ADVANCE_RXQ(sc); 5340286441Srpaulo } 5341286441Srpaulo 5342286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 5343286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 5344286441Srpaulo 5345286441Srpaulo /* 5346286441Srpaulo * Tell the firmware what we have processed. 5347286441Srpaulo * Seems like the hardware gets upset unless we align 5348286441Srpaulo * the write by 8?? 5349286441Srpaulo */ 5350286441Srpaulo hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; 5351286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); 5352286441Srpaulo} 5353286441Srpaulo 5354286441Srpaulostatic void 5355286441Srpauloiwm_intr(void *arg) 5356286441Srpaulo{ 5357286441Srpaulo struct iwm_softc *sc = arg; 5358286441Srpaulo int handled = 0; 5359286441Srpaulo int r1, r2, rv = 0; 5360286441Srpaulo int isperiodic = 0; 5361286441Srpaulo 5362286441Srpaulo IWM_LOCK(sc); 5363286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 5364286441Srpaulo 5365286441Srpaulo if (sc->sc_flags & IWM_FLAG_USE_ICT) { 5366286441Srpaulo uint32_t *ict = sc->ict_dma.vaddr; 5367286441Srpaulo int tmp; 5368286441Srpaulo 5369286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5370286441Srpaulo if (!tmp) 5371286441Srpaulo goto out_ena; 5372286441Srpaulo 5373286441Srpaulo /* 5374286441Srpaulo * ok, there was something. keep plowing until we have all. 5375286441Srpaulo */ 5376286441Srpaulo r1 = r2 = 0; 5377286441Srpaulo while (tmp) { 5378286441Srpaulo r1 |= tmp; 5379286441Srpaulo ict[sc->ict_cur] = 0; 5380286441Srpaulo sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT; 5381286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5382286441Srpaulo } 5383286441Srpaulo 5384286441Srpaulo /* this is where the fun begins. don't ask */ 5385286441Srpaulo if (r1 == 0xffffffff) 5386286441Srpaulo r1 = 0; 5387286441Srpaulo 5388286441Srpaulo /* i am not expected to understand this */ 5389286441Srpaulo if (r1 & 0xc0000) 5390286441Srpaulo r1 |= 0x8000; 5391286441Srpaulo r1 = (0xff & r1) | ((0xff00 & r1) << 16); 5392286441Srpaulo } else { 5393286441Srpaulo r1 = IWM_READ(sc, IWM_CSR_INT); 5394286441Srpaulo /* "hardware gone" (where, fishing?) */ 5395286441Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 5396286441Srpaulo goto out; 5397286441Srpaulo r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); 5398286441Srpaulo } 5399286441Srpaulo if (r1 == 0 && r2 == 0) { 5400286441Srpaulo goto out_ena; 5401286441Srpaulo } 5402286441Srpaulo 5403286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); 5404286441Srpaulo 5405286441Srpaulo /* ignored */ 5406286441Srpaulo handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); 5407286441Srpaulo 5408286441Srpaulo if (r1 & IWM_CSR_INT_BIT_SW_ERR) { 5409286441Srpaulo int i; 5410287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5411286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5412286441Srpaulo 5413298793Sdchagin#ifdef IWM_DEBUG 5414286441Srpaulo iwm_nic_error(sc); 5415298793Sdchagin#endif 5416286441Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 5417286441Srpaulo device_printf(sc->sc_dev, "driver status:\n"); 5418286441Srpaulo for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) { 5419286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[i]; 5420286441Srpaulo device_printf(sc->sc_dev, 5421286441Srpaulo " tx ring %2d: qid=%-2d cur=%-3d " 5422286441Srpaulo "queued=%-3d\n", 5423286441Srpaulo i, ring->qid, ring->cur, ring->queued); 5424286441Srpaulo } 5425286441Srpaulo device_printf(sc->sc_dev, 5426286441Srpaulo " rx ring: cur=%d\n", sc->rxq.cur); 5427286441Srpaulo device_printf(sc->sc_dev, 5428298659Scem " 802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state); 5429286441Srpaulo 5430298594Sadrian /* Don't stop the device; just do a VAP restart */ 5431298594Sadrian IWM_UNLOCK(sc); 5432286441Srpaulo 5433298594Sadrian if (vap == NULL) { 5434298594Sadrian printf("%s: null vap\n", __func__); 5435298594Sadrian return; 5436298594Sadrian } 5437298594Sadrian 5438298594Sadrian device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " 5439298594Sadrian "restarting\n", __func__, vap->iv_state); 5440298594Sadrian 5441298594Sadrian /* XXX TODO: turn this into a callout/taskqueue */ 5442298594Sadrian ieee80211_restart_all(ic); 5443298594Sadrian return; 5444286441Srpaulo } 5445286441Srpaulo 5446286441Srpaulo if (r1 & IWM_CSR_INT_BIT_HW_ERR) { 5447286441Srpaulo handled |= IWM_CSR_INT_BIT_HW_ERR; 5448286441Srpaulo device_printf(sc->sc_dev, "hardware error, stopping device\n"); 5449287197Sglebius iwm_stop(sc); 5450286441Srpaulo rv = 1; 5451286441Srpaulo goto out; 5452286441Srpaulo } 5453286441Srpaulo 5454286441Srpaulo /* firmware chunk loaded */ 5455286441Srpaulo if (r1 & IWM_CSR_INT_BIT_FH_TX) { 5456286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); 5457286441Srpaulo handled |= IWM_CSR_INT_BIT_FH_TX; 5458286441Srpaulo sc->sc_fw_chunk_done = 1; 5459286441Srpaulo wakeup(&sc->sc_fw); 5460286441Srpaulo } 5461286441Srpaulo 5462286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RF_KILL) { 5463286441Srpaulo handled |= IWM_CSR_INT_BIT_RF_KILL; 5464287197Sglebius if (iwm_check_rfkill(sc)) { 5465286441Srpaulo device_printf(sc->sc_dev, 5466286441Srpaulo "%s: rfkill switch, disabling interface\n", 5467286441Srpaulo __func__); 5468287197Sglebius iwm_stop(sc); 5469286441Srpaulo } 5470286441Srpaulo } 5471286441Srpaulo 5472286441Srpaulo /* 5473286441Srpaulo * The Linux driver uses periodic interrupts to avoid races. 5474286441Srpaulo * We cargo-cult like it's going out of fashion. 5475286441Srpaulo */ 5476286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { 5477286441Srpaulo handled |= IWM_CSR_INT_BIT_RX_PERIODIC; 5478286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); 5479286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) 5480286441Srpaulo IWM_WRITE_1(sc, 5481286441Srpaulo IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); 5482286441Srpaulo isperiodic = 1; 5483286441Srpaulo } 5484286441Srpaulo 5485286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) { 5486286441Srpaulo handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX); 5487286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); 5488286441Srpaulo 5489286441Srpaulo iwm_notif_intr(sc); 5490286441Srpaulo 5491286441Srpaulo /* enable periodic interrupt, see above */ 5492286441Srpaulo if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic) 5493286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, 5494286441Srpaulo IWM_CSR_INT_PERIODIC_ENA); 5495286441Srpaulo } 5496286441Srpaulo 5497286441Srpaulo if (__predict_false(r1 & ~handled)) 5498286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5499286441Srpaulo "%s: unhandled interrupts: %x\n", __func__, r1); 5500286441Srpaulo rv = 1; 5501286441Srpaulo 5502286441Srpaulo out_ena: 5503286441Srpaulo iwm_restore_interrupts(sc); 5504286441Srpaulo out: 5505286441Srpaulo IWM_UNLOCK(sc); 5506286441Srpaulo return; 5507286441Srpaulo} 5508286441Srpaulo 5509286441Srpaulo/* 5510286441Srpaulo * Autoconf glue-sniffing 5511286441Srpaulo */ 5512286441Srpaulo#define PCI_VENDOR_INTEL 0x8086 5513286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_1 0x08b3 5514286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_2 0x08b4 5515303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_1 0x3165 5516303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_2 0x3166 5517286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_1 0x08b1 5518286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_2 0x08b2 5519286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_1 0x095a 5520286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_2 0x095b 5521303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_1 0x24f3 5522303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_2 0x24f4 5523286441Srpaulo 5524286441Srpaulostatic const struct iwm_devices { 5525286441Srpaulo uint16_t device; 5526286441Srpaulo const char *name; 5527286441Srpaulo} iwm_devices[] = { 5528286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" }, 5529286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" }, 5530303628Ssbruno { PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" }, 5531303628Ssbruno { PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" }, 5532286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" }, 5533286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" }, 5534286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" }, 5535286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" }, 5536303628Ssbruno { PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" }, 5537303628Ssbruno { PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" }, 5538286441Srpaulo}; 5539286441Srpaulo 5540286441Srpaulostatic int 5541286441Srpauloiwm_probe(device_t dev) 5542286441Srpaulo{ 5543286441Srpaulo int i; 5544286441Srpaulo 5545303628Ssbruno for (i = 0; i < nitems(iwm_devices); i++) { 5546286441Srpaulo if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && 5547286441Srpaulo pci_get_device(dev) == iwm_devices[i].device) { 5548286441Srpaulo device_set_desc(dev, iwm_devices[i].name); 5549286441Srpaulo return (BUS_PROBE_DEFAULT); 5550286441Srpaulo } 5551303628Ssbruno } 5552286441Srpaulo 5553286441Srpaulo return (ENXIO); 5554286441Srpaulo} 5555286441Srpaulo 5556286441Srpaulostatic int 5557286441Srpauloiwm_dev_check(device_t dev) 5558286441Srpaulo{ 5559286441Srpaulo struct iwm_softc *sc; 5560286441Srpaulo 5561286441Srpaulo sc = device_get_softc(dev); 5562286441Srpaulo 5563303628Ssbruno sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); 5564286441Srpaulo switch (pci_get_device(dev)) { 5565286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_1: 5566286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_2: 5567286441Srpaulo sc->sc_fwname = "iwm3160fw"; 5568286441Srpaulo sc->host_interrupt_operation_mode = 1; 5569303628Ssbruno sc->sc_device_family = IWM_DEVICE_FAMILY_7000; 5570303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5571286441Srpaulo return (0); 5572303628Ssbruno case PCI_PRODUCT_INTEL_WL_3165_1: 5573303628Ssbruno case PCI_PRODUCT_INTEL_WL_3165_2: 5574303628Ssbruno sc->sc_fwname = "iwm7265fw"; 5575303628Ssbruno sc->host_interrupt_operation_mode = 0; 5576303628Ssbruno sc->sc_device_family = IWM_DEVICE_FAMILY_7000; 5577303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5578303628Ssbruno return (0); 5579286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_1: 5580286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_2: 5581286441Srpaulo sc->sc_fwname = "iwm7260fw"; 5582286441Srpaulo sc->host_interrupt_operation_mode = 1; 5583303628Ssbruno sc->sc_device_family = IWM_DEVICE_FAMILY_7000; 5584303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5585286441Srpaulo return (0); 5586286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_1: 5587286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_2: 5588286441Srpaulo sc->sc_fwname = "iwm7265fw"; 5589286441Srpaulo sc->host_interrupt_operation_mode = 0; 5590303628Ssbruno sc->sc_device_family = IWM_DEVICE_FAMILY_7000; 5591303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5592286441Srpaulo return (0); 5593303628Ssbruno case PCI_PRODUCT_INTEL_WL_8260_1: 5594303628Ssbruno case PCI_PRODUCT_INTEL_WL_8260_2: 5595303628Ssbruno sc->sc_fwname = "iwm8000Cfw"; 5596303628Ssbruno sc->host_interrupt_operation_mode = 0; 5597303628Ssbruno sc->sc_device_family = IWM_DEVICE_FAMILY_8000; 5598303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; 5599303628Ssbruno return (0); 5600286441Srpaulo default: 5601286441Srpaulo device_printf(dev, "unknown adapter type\n"); 5602286441Srpaulo return ENXIO; 5603286441Srpaulo } 5604286441Srpaulo} 5605286441Srpaulo 5606330140Seadler/* PCI registers */ 5607330140Seadler#define PCI_CFG_RETRY_TIMEOUT 0x041 5608330140Seadler 5609286441Srpaulostatic int 5610286441Srpauloiwm_pci_attach(device_t dev) 5611286441Srpaulo{ 5612286441Srpaulo struct iwm_softc *sc; 5613286441Srpaulo int count, error, rid; 5614286441Srpaulo uint16_t reg; 5615286441Srpaulo 5616286441Srpaulo sc = device_get_softc(dev); 5617286441Srpaulo 5618330140Seadler /* We disable the RETRY_TIMEOUT register (0x41) to keep 5619330140Seadler * PCI Tx retries from interfering with C3 CPU state */ 5620330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 5621286441Srpaulo 5622286441Srpaulo /* Enable bus-mastering and hardware bug workaround. */ 5623286441Srpaulo pci_enable_busmaster(dev); 5624286441Srpaulo reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg)); 5625286441Srpaulo /* if !MSI */ 5626286441Srpaulo if (reg & PCIM_STATUS_INTxSTATE) { 5627286441Srpaulo reg &= ~PCIM_STATUS_INTxSTATE; 5628286441Srpaulo } 5629286441Srpaulo pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg)); 5630286441Srpaulo 5631286441Srpaulo rid = PCIR_BAR(0); 5632286441Srpaulo sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 5633286441Srpaulo RF_ACTIVE); 5634286441Srpaulo if (sc->sc_mem == NULL) { 5635286441Srpaulo device_printf(sc->sc_dev, "can't map mem space\n"); 5636286441Srpaulo return (ENXIO); 5637286441Srpaulo } 5638286441Srpaulo sc->sc_st = rman_get_bustag(sc->sc_mem); 5639286441Srpaulo sc->sc_sh = rman_get_bushandle(sc->sc_mem); 5640286441Srpaulo 5641286441Srpaulo /* Install interrupt handler. */ 5642286441Srpaulo count = 1; 5643286441Srpaulo rid = 0; 5644286441Srpaulo if (pci_alloc_msi(dev, &count) == 0) 5645286441Srpaulo rid = 1; 5646286441Srpaulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 5647286441Srpaulo (rid != 0 ? 0 : RF_SHAREABLE)); 5648286441Srpaulo if (sc->sc_irq == NULL) { 5649286441Srpaulo device_printf(dev, "can't map interrupt\n"); 5650286441Srpaulo return (ENXIO); 5651286441Srpaulo } 5652286441Srpaulo error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 5653286441Srpaulo NULL, iwm_intr, sc, &sc->sc_ih); 5654286441Srpaulo if (sc->sc_ih == NULL) { 5655286441Srpaulo device_printf(dev, "can't establish interrupt"); 5656286441Srpaulo return (ENXIO); 5657286441Srpaulo } 5658286441Srpaulo sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); 5659286441Srpaulo 5660286441Srpaulo return (0); 5661286441Srpaulo} 5662286441Srpaulo 5663286441Srpaulostatic void 5664286441Srpauloiwm_pci_detach(device_t dev) 5665286441Srpaulo{ 5666286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 5667286441Srpaulo 5668286441Srpaulo if (sc->sc_irq != NULL) { 5669286441Srpaulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 5670286441Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 5671286441Srpaulo rman_get_rid(sc->sc_irq), sc->sc_irq); 5672286441Srpaulo pci_release_msi(dev); 5673286441Srpaulo } 5674286441Srpaulo if (sc->sc_mem != NULL) 5675286441Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, 5676286441Srpaulo rman_get_rid(sc->sc_mem), sc->sc_mem); 5677286441Srpaulo} 5678286441Srpaulo 5679286441Srpaulo 5680286441Srpaulo 5681286441Srpaulostatic int 5682286441Srpauloiwm_attach(device_t dev) 5683286441Srpaulo{ 5684287197Sglebius struct iwm_softc *sc = device_get_softc(dev); 5685287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5686286441Srpaulo int error; 5687286441Srpaulo int txq_i, i; 5688286441Srpaulo 5689286441Srpaulo sc->sc_dev = dev; 5690293099Savos IWM_LOCK_INIT(sc); 5691287197Sglebius mbufq_init(&sc->sc_snd, ifqmaxlen); 5692286441Srpaulo callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); 5693301187Sadrian callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0); 5694286441Srpaulo TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc); 5695286441Srpaulo 5696286441Srpaulo /* PCI attach */ 5697286441Srpaulo error = iwm_pci_attach(dev); 5698286441Srpaulo if (error != 0) 5699286441Srpaulo goto fail; 5700286441Srpaulo 5701286441Srpaulo sc->sc_wantresp = -1; 5702286441Srpaulo 5703286441Srpaulo /* Check device type */ 5704286441Srpaulo error = iwm_dev_check(dev); 5705286441Srpaulo if (error != 0) 5706286441Srpaulo goto fail; 5707286441Srpaulo 5708286441Srpaulo /* 5709286441Srpaulo * We now start fiddling with the hardware 5710286441Srpaulo */ 5711303628Ssbruno /* 5712303628Ssbruno * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have 5713303628Ssbruno * changed, and now the revision step also includes bit 0-1 (no more 5714303628Ssbruno * "dash" value). To keep hw_rev backwards compatible - we'll store it 5715303628Ssbruno * in the old format. 5716303628Ssbruno */ 5717303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) 5718303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | 5719303628Ssbruno (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); 5720303628Ssbruno 5721286441Srpaulo if (iwm_prepare_card_hw(sc) != 0) { 5722286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 5723286441Srpaulo goto fail; 5724286441Srpaulo } 5725286441Srpaulo 5726303628Ssbruno if (sc->sc_device_family == IWM_DEVICE_FAMILY_8000) { 5727303628Ssbruno int ret; 5728303628Ssbruno uint32_t hw_step; 5729303628Ssbruno 5730303628Ssbruno /* 5731303628Ssbruno * In order to recognize C step the driver should read the 5732303628Ssbruno * chip version id located at the AUX bus MISC address. 5733303628Ssbruno */ 5734303628Ssbruno IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, 5735303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 5736303628Ssbruno DELAY(2); 5737303628Ssbruno 5738303628Ssbruno ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, 5739303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 5740303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 5741303628Ssbruno 25000); 5742330149Seadler if (!ret) { 5743303628Ssbruno device_printf(sc->sc_dev, 5744303628Ssbruno "Failed to wake up the nic\n"); 5745303628Ssbruno goto fail; 5746303628Ssbruno } 5747303628Ssbruno 5748303628Ssbruno if (iwm_nic_lock(sc)) { 5749303628Ssbruno hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); 5750303628Ssbruno hw_step |= IWM_ENABLE_WFPM; 5751303628Ssbruno iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); 5752303628Ssbruno hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); 5753303628Ssbruno hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; 5754303628Ssbruno if (hw_step == 0x3) 5755303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | 5756303628Ssbruno (IWM_SILICON_C_STEP << 2); 5757303628Ssbruno iwm_nic_unlock(sc); 5758303628Ssbruno } else { 5759303628Ssbruno device_printf(sc->sc_dev, "Failed to lock the nic\n"); 5760303628Ssbruno goto fail; 5761303628Ssbruno } 5762303628Ssbruno } 5763303628Ssbruno 5764286441Srpaulo /* Allocate DMA memory for firmware transfers. */ 5765286441Srpaulo if ((error = iwm_alloc_fwmem(sc)) != 0) { 5766286441Srpaulo device_printf(dev, "could not allocate memory for firmware\n"); 5767286441Srpaulo goto fail; 5768286441Srpaulo } 5769286441Srpaulo 5770286441Srpaulo /* Allocate "Keep Warm" page. */ 5771286441Srpaulo if ((error = iwm_alloc_kw(sc)) != 0) { 5772286441Srpaulo device_printf(dev, "could not allocate keep warm page\n"); 5773286441Srpaulo goto fail; 5774286441Srpaulo } 5775286441Srpaulo 5776286441Srpaulo /* We use ICT interrupts */ 5777286441Srpaulo if ((error = iwm_alloc_ict(sc)) != 0) { 5778286441Srpaulo device_printf(dev, "could not allocate ICT table\n"); 5779286441Srpaulo goto fail; 5780286441Srpaulo } 5781286441Srpaulo 5782286441Srpaulo /* Allocate TX scheduler "rings". */ 5783286441Srpaulo if ((error = iwm_alloc_sched(sc)) != 0) { 5784286441Srpaulo device_printf(dev, "could not allocate TX scheduler rings\n"); 5785286441Srpaulo goto fail; 5786286441Srpaulo } 5787286441Srpaulo 5788286441Srpaulo /* Allocate TX rings */ 5789286441Srpaulo for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) { 5790286441Srpaulo if ((error = iwm_alloc_tx_ring(sc, 5791286441Srpaulo &sc->txq[txq_i], txq_i)) != 0) { 5792286441Srpaulo device_printf(dev, 5793286441Srpaulo "could not allocate TX ring %d\n", 5794286441Srpaulo txq_i); 5795286441Srpaulo goto fail; 5796286441Srpaulo } 5797286441Srpaulo } 5798286441Srpaulo 5799286441Srpaulo /* Allocate RX ring. */ 5800286441Srpaulo if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) { 5801286441Srpaulo device_printf(dev, "could not allocate RX ring\n"); 5802286441Srpaulo goto fail; 5803286441Srpaulo } 5804286441Srpaulo 5805286441Srpaulo /* Clear pending interrupts. */ 5806286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); 5807286441Srpaulo 5808286441Srpaulo ic->ic_softc = sc; 5809286441Srpaulo ic->ic_name = device_get_nameunit(sc->sc_dev); 5810286441Srpaulo ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 5811286441Srpaulo ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 5812286441Srpaulo 5813286441Srpaulo /* Set device capabilities. */ 5814286441Srpaulo ic->ic_caps = 5815286441Srpaulo IEEE80211_C_STA | 5816286441Srpaulo IEEE80211_C_WPA | /* WPA/RSN */ 5817286441Srpaulo IEEE80211_C_WME | 5818286441Srpaulo IEEE80211_C_SHSLOT | /* short slot time supported */ 5819286441Srpaulo IEEE80211_C_SHPREAMBLE /* short preamble supported */ 5820286441Srpaulo// IEEE80211_C_BGSCAN /* capable of bg scanning */ 5821286441Srpaulo ; 5822286441Srpaulo for (i = 0; i < nitems(sc->sc_phyctxt); i++) { 5823286441Srpaulo sc->sc_phyctxt[i].id = i; 5824286441Srpaulo sc->sc_phyctxt[i].color = 0; 5825286441Srpaulo sc->sc_phyctxt[i].ref = 0; 5826286441Srpaulo sc->sc_phyctxt[i].channel = NULL; 5827286441Srpaulo } 5828286441Srpaulo 5829330144Seadler /* Default noise floor */ 5830330144Seadler sc->sc_noise = -96; 5831330144Seadler 5832286441Srpaulo /* Max RSSI */ 5833286441Srpaulo sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; 5834330144Seadler 5835286441Srpaulo sc->sc_preinit_hook.ich_func = iwm_preinit; 5836286441Srpaulo sc->sc_preinit_hook.ich_arg = sc; 5837286441Srpaulo if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { 5838286441Srpaulo device_printf(dev, "config_intrhook_establish failed\n"); 5839286441Srpaulo goto fail; 5840286441Srpaulo } 5841286441Srpaulo 5842286441Srpaulo#ifdef IWM_DEBUG 5843286441Srpaulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 5844286441Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", 5845286441Srpaulo CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); 5846286441Srpaulo#endif 5847286441Srpaulo 5848286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 5849286441Srpaulo "<-%s\n", __func__); 5850286441Srpaulo 5851286441Srpaulo return 0; 5852286441Srpaulo 5853286441Srpaulo /* Free allocated memory if something failed during attachment. */ 5854286441Srpaulofail: 5855286441Srpaulo iwm_detach_local(sc, 0); 5856286441Srpaulo 5857286441Srpaulo return ENXIO; 5858286441Srpaulo} 5859286441Srpaulo 5860286441Srpaulostatic int 5861303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr) 5862303628Ssbruno{ 5863303628Ssbruno char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; 5864303628Ssbruno 5865303628Ssbruno if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr)) 5866303628Ssbruno return (FALSE); 5867303628Ssbruno 5868303628Ssbruno return (TRUE); 5869303628Ssbruno} 5870303628Ssbruno 5871303628Ssbrunostatic int 5872286441Srpauloiwm_update_edca(struct ieee80211com *ic) 5873286441Srpaulo{ 5874286865Sadrian struct iwm_softc *sc = ic->ic_softc; 5875286441Srpaulo 5876286441Srpaulo device_printf(sc->sc_dev, "%s: called\n", __func__); 5877286441Srpaulo return (0); 5878286441Srpaulo} 5879286441Srpaulo 5880286441Srpaulostatic void 5881286441Srpauloiwm_preinit(void *arg) 5882286441Srpaulo{ 5883286441Srpaulo struct iwm_softc *sc = arg; 5884286441Srpaulo device_t dev = sc->sc_dev; 5885287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5886286441Srpaulo int error; 5887286441Srpaulo 5888286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 5889286441Srpaulo "->%s\n", __func__); 5890286441Srpaulo 5891286441Srpaulo IWM_LOCK(sc); 5892286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 5893286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 5894286441Srpaulo IWM_UNLOCK(sc); 5895286441Srpaulo goto fail; 5896286441Srpaulo } 5897286441Srpaulo 5898286441Srpaulo error = iwm_run_init_mvm_ucode(sc, 1); 5899286441Srpaulo iwm_stop_device(sc); 5900286441Srpaulo if (error) { 5901286441Srpaulo IWM_UNLOCK(sc); 5902286441Srpaulo goto fail; 5903286441Srpaulo } 5904286441Srpaulo device_printf(dev, 5905303628Ssbruno "hw rev 0x%x, fw ver %s, address %s\n", 5906286441Srpaulo sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, 5907303628Ssbruno sc->sc_fwver, ether_sprintf(sc->sc_nvm.hw_addr)); 5908286441Srpaulo 5909286441Srpaulo /* not all hardware can do 5GHz band */ 5910286441Srpaulo if (!sc->sc_nvm.sku_cap_band_52GHz_enable) 5911286441Srpaulo memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, 5912286441Srpaulo sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A])); 5913286441Srpaulo IWM_UNLOCK(sc); 5914286441Srpaulo 5915298877Savos iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, 5916298877Savos ic->ic_channels); 5917298877Savos 5918286441Srpaulo /* 5919286441Srpaulo * At this point we've committed - if we fail to do setup, 5920286441Srpaulo * we now also have to tear down the net80211 state. 5921286441Srpaulo */ 5922287197Sglebius ieee80211_ifattach(ic); 5923286441Srpaulo ic->ic_vap_create = iwm_vap_create; 5924286441Srpaulo ic->ic_vap_delete = iwm_vap_delete; 5925286441Srpaulo ic->ic_raw_xmit = iwm_raw_xmit; 5926286441Srpaulo ic->ic_node_alloc = iwm_node_alloc; 5927286441Srpaulo ic->ic_scan_start = iwm_scan_start; 5928286441Srpaulo ic->ic_scan_end = iwm_scan_end; 5929286441Srpaulo ic->ic_update_mcast = iwm_update_mcast; 5930298877Savos ic->ic_getradiocaps = iwm_init_channel_map; 5931286441Srpaulo ic->ic_set_channel = iwm_set_channel; 5932286441Srpaulo ic->ic_scan_curchan = iwm_scan_curchan; 5933286441Srpaulo ic->ic_scan_mindwell = iwm_scan_mindwell; 5934286441Srpaulo ic->ic_wme.wme_update = iwm_update_edca; 5935287197Sglebius ic->ic_parent = iwm_parent; 5936287197Sglebius ic->ic_transmit = iwm_transmit; 5937286441Srpaulo iwm_radiotap_attach(sc); 5938286441Srpaulo if (bootverbose) 5939286441Srpaulo ieee80211_announce(ic); 5940286441Srpaulo 5941286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 5942286441Srpaulo "<-%s\n", __func__); 5943286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 5944286441Srpaulo 5945286441Srpaulo return; 5946286441Srpaulofail: 5947286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 5948286441Srpaulo iwm_detach_local(sc, 0); 5949286441Srpaulo} 5950286441Srpaulo 5951286441Srpaulo/* 5952286441Srpaulo * Attach the interface to 802.11 radiotap. 5953286441Srpaulo */ 5954286441Srpaulostatic void 5955286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc) 5956286441Srpaulo{ 5957287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5958286441Srpaulo 5959286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 5960286441Srpaulo "->%s begin\n", __func__); 5961286441Srpaulo ieee80211_radiotap_attach(ic, 5962286441Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 5963286441Srpaulo IWM_TX_RADIOTAP_PRESENT, 5964286441Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 5965286441Srpaulo IWM_RX_RADIOTAP_PRESENT); 5966286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 5967286441Srpaulo "->%s end\n", __func__); 5968286441Srpaulo} 5969286441Srpaulo 5970286441Srpaulostatic struct ieee80211vap * 5971286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 5972286441Srpaulo enum ieee80211_opmode opmode, int flags, 5973286441Srpaulo const uint8_t bssid[IEEE80211_ADDR_LEN], 5974286441Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]) 5975286441Srpaulo{ 5976286441Srpaulo struct iwm_vap *ivp; 5977286441Srpaulo struct ieee80211vap *vap; 5978286441Srpaulo 5979286441Srpaulo if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 5980286441Srpaulo return NULL; 5981287197Sglebius ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO); 5982286441Srpaulo vap = &ivp->iv_vap; 5983287197Sglebius ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); 5984286441Srpaulo vap->iv_bmissthreshold = 10; /* override default */ 5985286441Srpaulo /* Override with driver methods. */ 5986286441Srpaulo ivp->iv_newstate = vap->iv_newstate; 5987286441Srpaulo vap->iv_newstate = iwm_newstate; 5988286441Srpaulo 5989286441Srpaulo ieee80211_ratectl_init(vap); 5990286441Srpaulo /* Complete setup. */ 5991287197Sglebius ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status, 5992287197Sglebius mac); 5993286441Srpaulo ic->ic_opmode = opmode; 5994286441Srpaulo 5995286441Srpaulo return vap; 5996286441Srpaulo} 5997286441Srpaulo 5998286441Srpaulostatic void 5999286441Srpauloiwm_vap_delete(struct ieee80211vap *vap) 6000286441Srpaulo{ 6001286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 6002286441Srpaulo 6003286441Srpaulo ieee80211_ratectl_deinit(vap); 6004286441Srpaulo ieee80211_vap_detach(vap); 6005286441Srpaulo free(ivp, M_80211_VAP); 6006286441Srpaulo} 6007286441Srpaulo 6008286441Srpaulostatic void 6009286441Srpauloiwm_scan_start(struct ieee80211com *ic) 6010286441Srpaulo{ 6011286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6012303628Ssbruno struct iwm_softc *sc = ic->ic_softc; 6013286441Srpaulo int error; 6014286441Srpaulo 6015286441Srpaulo IWM_LOCK(sc); 6016303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) 6017303628Ssbruno error = iwm_mvm_umac_scan(sc); 6018303628Ssbruno else 6019303628Ssbruno error = iwm_mvm_lmac_scan(sc); 6020303628Ssbruno if (error != 0) { 6021301190Sadrian device_printf(sc->sc_dev, "could not initiate 2 GHz scan\n"); 6022286441Srpaulo IWM_UNLOCK(sc); 6023286441Srpaulo ieee80211_cancel_scan(vap); 6024301187Sadrian } else { 6025301187Sadrian iwm_led_blink_start(sc); 6026286441Srpaulo IWM_UNLOCK(sc); 6027301187Sadrian } 6028286441Srpaulo} 6029286441Srpaulo 6030286441Srpaulostatic void 6031286441Srpauloiwm_scan_end(struct ieee80211com *ic) 6032286441Srpaulo{ 6033301187Sadrian struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6034301187Sadrian struct iwm_softc *sc = ic->ic_softc; 6035301187Sadrian 6036301187Sadrian IWM_LOCK(sc); 6037301187Sadrian iwm_led_blink_stop(sc); 6038301187Sadrian if (vap->iv_state == IEEE80211_S_RUN) 6039301187Sadrian iwm_mvm_led_enable(sc); 6040301187Sadrian IWM_UNLOCK(sc); 6041286441Srpaulo} 6042286441Srpaulo 6043286441Srpaulostatic void 6044286441Srpauloiwm_update_mcast(struct ieee80211com *ic) 6045286441Srpaulo{ 6046286441Srpaulo} 6047286441Srpaulo 6048286441Srpaulostatic void 6049286441Srpauloiwm_set_channel(struct ieee80211com *ic) 6050286441Srpaulo{ 6051286441Srpaulo} 6052286441Srpaulo 6053286441Srpaulostatic void 6054286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 6055286441Srpaulo{ 6056286441Srpaulo} 6057286441Srpaulo 6058286441Srpaulostatic void 6059286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss) 6060286441Srpaulo{ 6061286441Srpaulo return; 6062286441Srpaulo} 6063286441Srpaulo 6064286441Srpaulovoid 6065286441Srpauloiwm_init_task(void *arg1) 6066286441Srpaulo{ 6067286441Srpaulo struct iwm_softc *sc = arg1; 6068286441Srpaulo 6069286441Srpaulo IWM_LOCK(sc); 6070286441Srpaulo while (sc->sc_flags & IWM_FLAG_BUSY) 6071286441Srpaulo msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0); 6072286441Srpaulo sc->sc_flags |= IWM_FLAG_BUSY; 6073287197Sglebius iwm_stop(sc); 6074287197Sglebius if (sc->sc_ic.ic_nrunning > 0) 6075286441Srpaulo iwm_init(sc); 6076286441Srpaulo sc->sc_flags &= ~IWM_FLAG_BUSY; 6077286441Srpaulo wakeup(&sc->sc_flags); 6078286441Srpaulo IWM_UNLOCK(sc); 6079286441Srpaulo} 6080286441Srpaulo 6081286441Srpaulostatic int 6082286441Srpauloiwm_resume(device_t dev) 6083286441Srpaulo{ 6084298612Sadrian struct iwm_softc *sc = device_get_softc(dev); 6085298612Sadrian int do_reinit = 0; 6086286441Srpaulo 6087330140Seadler /* 6088330140Seadler * We disable the RETRY_TIMEOUT register (0x41) to keep 6089330140Seadler * PCI Tx retries from interfering with C3 CPU state. 6090330140Seadler */ 6091330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 6092286441Srpaulo iwm_init_task(device_get_softc(dev)); 6093286441Srpaulo 6094298612Sadrian IWM_LOCK(sc); 6095303628Ssbruno if (sc->sc_flags & IWM_FLAG_SCANNING) { 6096303628Ssbruno sc->sc_flags &= ~IWM_FLAG_SCANNING; 6097298612Sadrian do_reinit = 1; 6098298612Sadrian } 6099298612Sadrian IWM_UNLOCK(sc); 6100298612Sadrian 6101298612Sadrian if (do_reinit) 6102298612Sadrian ieee80211_resume_all(&sc->sc_ic); 6103298612Sadrian 6104286441Srpaulo return 0; 6105286441Srpaulo} 6106286441Srpaulo 6107286441Srpaulostatic int 6108286441Srpauloiwm_suspend(device_t dev) 6109286441Srpaulo{ 6110298612Sadrian int do_stop = 0; 6111286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6112286441Srpaulo 6113298612Sadrian do_stop = !! (sc->sc_ic.ic_nrunning > 0); 6114298612Sadrian 6115298612Sadrian ieee80211_suspend_all(&sc->sc_ic); 6116298612Sadrian 6117298612Sadrian if (do_stop) { 6118287197Sglebius IWM_LOCK(sc); 6119287197Sglebius iwm_stop(sc); 6120303628Ssbruno sc->sc_flags |= IWM_FLAG_SCANNING; 6121287197Sglebius IWM_UNLOCK(sc); 6122287197Sglebius } 6123286441Srpaulo 6124286441Srpaulo return (0); 6125286441Srpaulo} 6126286441Srpaulo 6127286441Srpaulostatic int 6128286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211) 6129286441Srpaulo{ 6130293219Savos struct iwm_fw_info *fw = &sc->sc_fw; 6131286441Srpaulo device_t dev = sc->sc_dev; 6132286441Srpaulo int i; 6133286441Srpaulo 6134303628Ssbruno ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task); 6135303628Ssbruno 6136301187Sadrian callout_drain(&sc->sc_led_blink_to); 6137287197Sglebius callout_drain(&sc->sc_watchdog_to); 6138287197Sglebius iwm_stop_device(sc); 6139303628Ssbruno if (do_net80211) { 6140287197Sglebius ieee80211_ifdetach(&sc->sc_ic); 6141303628Ssbruno } 6142286441Srpaulo 6143302103Sadrian iwm_phy_db_free(sc); 6144302103Sadrian 6145286441Srpaulo /* Free descriptor rings */ 6146301970Sadrian iwm_free_rx_ring(sc, &sc->rxq); 6147286441Srpaulo for (i = 0; i < nitems(sc->txq); i++) 6148286441Srpaulo iwm_free_tx_ring(sc, &sc->txq[i]); 6149286441Srpaulo 6150293219Savos /* Free firmware */ 6151293219Savos if (fw->fw_fp != NULL) 6152293219Savos iwm_fw_info_free(fw); 6153293219Savos 6154293178Savos /* Free scheduler */ 6155330150Seadler iwm_dma_contig_free(&sc->sched_dma); 6156330150Seadler iwm_dma_contig_free(&sc->ict_dma); 6157330150Seadler iwm_dma_contig_free(&sc->kw_dma); 6158330150Seadler iwm_dma_contig_free(&sc->fw_dma); 6159286441Srpaulo 6160286441Srpaulo /* Finished with the hardware - detach things */ 6161286441Srpaulo iwm_pci_detach(dev); 6162286441Srpaulo 6163287197Sglebius mbufq_drain(&sc->sc_snd); 6164293099Savos IWM_LOCK_DESTROY(sc); 6165286441Srpaulo 6166286441Srpaulo return (0); 6167286441Srpaulo} 6168286441Srpaulo 6169286441Srpaulostatic int 6170286441Srpauloiwm_detach(device_t dev) 6171286441Srpaulo{ 6172286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6173286441Srpaulo 6174286441Srpaulo return (iwm_detach_local(sc, 1)); 6175286441Srpaulo} 6176286441Srpaulo 6177286441Srpaulostatic device_method_t iwm_pci_methods[] = { 6178286441Srpaulo /* Device interface */ 6179286441Srpaulo DEVMETHOD(device_probe, iwm_probe), 6180286441Srpaulo DEVMETHOD(device_attach, iwm_attach), 6181286441Srpaulo DEVMETHOD(device_detach, iwm_detach), 6182286441Srpaulo DEVMETHOD(device_suspend, iwm_suspend), 6183286441Srpaulo DEVMETHOD(device_resume, iwm_resume), 6184286441Srpaulo 6185286441Srpaulo DEVMETHOD_END 6186286441Srpaulo}; 6187286441Srpaulo 6188286441Srpaulostatic driver_t iwm_pci_driver = { 6189286441Srpaulo "iwm", 6190286441Srpaulo iwm_pci_methods, 6191286441Srpaulo sizeof (struct iwm_softc) 6192286441Srpaulo}; 6193286441Srpaulo 6194286441Srpaulostatic devclass_t iwm_devclass; 6195286441Srpaulo 6196286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL); 6197286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1); 6198286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1); 6199286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1); 6200