if_iwm.c revision 330166
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 330166 2018-03-01 05:43:10Z 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 168330166Seadler#define IWM_NVM_HW_SECTION_NUM_FAMILY_7000 0 169330166Seadler#define IWM_NVM_HW_SECTION_NUM_FAMILY_8000 10 170330166Seadler 171330166Seadler/* lower blocks contain EEPROM image and calibration data */ 172330166Seadler#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000 (16 * 512 * sizeof(uint16_t)) /* 16 KB */ 173330166Seadler#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000 (32 * 512 * sizeof(uint16_t)) /* 32 KB */ 174330166Seadler 175330166Seadler#define IWM7260_FW "iwm7260fw" 176330166Seadler#define IWM3160_FW "iwm3160fw" 177330166Seadler#define IWM7265_FW "iwm7265fw" 178330166Seadler#define IWM7265D_FW "iwm7265Dfw" 179330166Seadler#define IWM8000_FW "iwm8000Cfw" 180330166Seadler 181330166Seadler#define IWM_DEVICE_7000_COMMON \ 182330166Seadler .device_family = IWM_DEVICE_FAMILY_7000, \ 183330166Seadler .eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000, \ 184330166Seadler .nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_7000 185330166Seadler 186330166Seadlerconst struct iwm_cfg iwm7260_cfg = { 187330166Seadler .fw_name = IWM7260_FW, 188330166Seadler IWM_DEVICE_7000_COMMON, 189330166Seadler .host_interrupt_operation_mode = 1, 190330166Seadler}; 191330166Seadler 192330166Seadlerconst struct iwm_cfg iwm3160_cfg = { 193330166Seadler .fw_name = IWM3160_FW, 194330166Seadler IWM_DEVICE_7000_COMMON, 195330166Seadler .host_interrupt_operation_mode = 1, 196330166Seadler}; 197330166Seadler 198330166Seadlerconst struct iwm_cfg iwm3165_cfg = { 199330166Seadler /* XXX IWM7265D_FW doesn't seem to work properly yet */ 200330166Seadler .fw_name = IWM7265_FW, 201330166Seadler IWM_DEVICE_7000_COMMON, 202330166Seadler .host_interrupt_operation_mode = 0, 203330166Seadler}; 204330166Seadler 205330166Seadlerconst struct iwm_cfg iwm7265_cfg = { 206330166Seadler .fw_name = IWM7265_FW, 207330166Seadler IWM_DEVICE_7000_COMMON, 208330166Seadler .host_interrupt_operation_mode = 0, 209330166Seadler}; 210330166Seadler 211330166Seadlerconst struct iwm_cfg iwm7265d_cfg = { 212330166Seadler /* XXX IWM7265D_FW doesn't seem to work properly yet */ 213330166Seadler .fw_name = IWM7265_FW, 214330166Seadler IWM_DEVICE_7000_COMMON, 215330166Seadler .host_interrupt_operation_mode = 0, 216330166Seadler}; 217330166Seadler 218330166Seadler#define IWM_DEVICE_8000_COMMON \ 219330166Seadler .device_family = IWM_DEVICE_FAMILY_8000, \ 220330166Seadler .eeprom_size = IWM_OTP_LOW_IMAGE_SIZE_FAMILY_8000, \ 221330166Seadler .nvm_hw_section_num = IWM_NVM_HW_SECTION_NUM_FAMILY_8000 222330166Seadler 223330166Seadlerconst struct iwm_cfg iwm8260_cfg = { 224330166Seadler .fw_name = IWM8000_FW, 225330166Seadler IWM_DEVICE_8000_COMMON, 226330166Seadler .host_interrupt_operation_mode = 0, 227330166Seadler}; 228330166Seadler 229286441Srpauloconst uint8_t iwm_nvm_channels[] = { 230286441Srpaulo /* 2.4 GHz */ 231286441Srpaulo 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 232286441Srpaulo /* 5 GHz */ 233298877Savos 36, 40, 44, 48, 52, 56, 60, 64, 234286441Srpaulo 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 235286441Srpaulo 149, 153, 157, 161, 165 236286441Srpaulo}; 237298877Savos_Static_assert(nitems(iwm_nvm_channels) <= IWM_NUM_CHANNELS, 238298877Savos "IWM_NUM_CHANNELS is too small"); 239298877Savos 240303628Ssbrunoconst uint8_t iwm_nvm_channels_8000[] = { 241303628Ssbruno /* 2.4 GHz */ 242303628Ssbruno 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 243303628Ssbruno /* 5 GHz */ 244303628Ssbruno 36, 40, 44, 48, 52, 56, 60, 64, 68, 72, 76, 80, 84, 88, 92, 245303628Ssbruno 96, 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 246303628Ssbruno 149, 153, 157, 161, 165, 169, 173, 177, 181 247303628Ssbruno}; 248303628Ssbruno_Static_assert(nitems(iwm_nvm_channels_8000) <= IWM_NUM_CHANNELS_8000, 249303628Ssbruno "IWM_NUM_CHANNELS_8000 is too small"); 250303628Ssbruno 251303628Ssbruno#define IWM_NUM_2GHZ_CHANNELS 14 252303628Ssbruno#define IWM_N_HW_ADDR_MASK 0xF 253303628Ssbruno 254286441Srpaulo/* 255286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries 256286441Srpaulo * that are populated. 257286441Srpaulo */ 258286441Srpauloconst struct iwm_rate { 259286441Srpaulo uint8_t rate; 260286441Srpaulo uint8_t plcp; 261286441Srpaulo} iwm_rates[] = { 262286441Srpaulo { 2, IWM_RATE_1M_PLCP }, 263286441Srpaulo { 4, IWM_RATE_2M_PLCP }, 264286441Srpaulo { 11, IWM_RATE_5M_PLCP }, 265286441Srpaulo { 22, IWM_RATE_11M_PLCP }, 266286441Srpaulo { 12, IWM_RATE_6M_PLCP }, 267286441Srpaulo { 18, IWM_RATE_9M_PLCP }, 268286441Srpaulo { 24, IWM_RATE_12M_PLCP }, 269286441Srpaulo { 36, IWM_RATE_18M_PLCP }, 270286441Srpaulo { 48, IWM_RATE_24M_PLCP }, 271286441Srpaulo { 72, IWM_RATE_36M_PLCP }, 272286441Srpaulo { 96, IWM_RATE_48M_PLCP }, 273286441Srpaulo { 108, IWM_RATE_54M_PLCP }, 274286441Srpaulo}; 275286441Srpaulo#define IWM_RIDX_CCK 0 276286441Srpaulo#define IWM_RIDX_OFDM 4 277286441Srpaulo#define IWM_RIDX_MAX (nitems(iwm_rates)-1) 278286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) 279286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) 280286441Srpaulo 281303628Ssbrunostruct iwm_nvm_section { 282303628Ssbruno uint16_t length; 283303628Ssbruno uint8_t *data; 284303628Ssbruno}; 285303628Ssbruno 286286441Srpaulostatic int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); 287286441Srpaulostatic int iwm_firmware_store_section(struct iwm_softc *, 288286441Srpaulo enum iwm_ucode_type, 289286441Srpaulo const uint8_t *, size_t); 290286441Srpaulostatic int iwm_set_default_calib(struct iwm_softc *, const void *); 291286441Srpaulostatic void iwm_fw_info_free(struct iwm_fw_info *); 292286441Srpaulostatic int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); 293286441Srpaulostatic void iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int); 294286441Srpaulostatic int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, 295286441Srpaulo bus_size_t, bus_size_t); 296286441Srpaulostatic void iwm_dma_contig_free(struct iwm_dma_info *); 297286441Srpaulostatic int iwm_alloc_fwmem(struct iwm_softc *); 298286441Srpaulostatic int iwm_alloc_sched(struct iwm_softc *); 299286441Srpaulostatic int iwm_alloc_kw(struct iwm_softc *); 300286441Srpaulostatic int iwm_alloc_ict(struct iwm_softc *); 301286441Srpaulostatic int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 302301191Sadrianstatic void iwm_disable_rx_dma(struct iwm_softc *); 303286441Srpaulostatic void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 304286441Srpaulostatic void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 305286441Srpaulostatic int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, 306286441Srpaulo int); 307286441Srpaulostatic void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 308286441Srpaulostatic void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 309286441Srpaulostatic void iwm_enable_interrupts(struct iwm_softc *); 310286441Srpaulostatic void iwm_restore_interrupts(struct iwm_softc *); 311286441Srpaulostatic void iwm_disable_interrupts(struct iwm_softc *); 312286441Srpaulostatic void iwm_ict_reset(struct iwm_softc *); 313286441Srpaulostatic int iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *); 314286441Srpaulostatic void iwm_stop_device(struct iwm_softc *); 315286441Srpaulostatic void iwm_mvm_nic_config(struct iwm_softc *); 316286441Srpaulostatic int iwm_nic_rx_init(struct iwm_softc *); 317286441Srpaulostatic int iwm_nic_tx_init(struct iwm_softc *); 318286441Srpaulostatic int iwm_nic_init(struct iwm_softc *); 319303628Ssbrunostatic int iwm_enable_txq(struct iwm_softc *, int, int, int); 320286441Srpaulostatic int iwm_post_alive(struct iwm_softc *); 321286441Srpaulostatic int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, 322286441Srpaulo uint16_t, uint8_t *, uint16_t *); 323286441Srpaulostatic int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, 324330165Seadler uint16_t *, uint32_t); 325298877Savosstatic uint32_t iwm_eeprom_channel_flags(uint16_t); 326298877Savosstatic void iwm_add_channel_band(struct iwm_softc *, 327303628Ssbruno struct ieee80211_channel[], int, int *, int, size_t, 328298877Savos const uint8_t[]); 329298877Savosstatic void iwm_init_channel_map(struct ieee80211com *, int, int *, 330298877Savos struct ieee80211_channel[]); 331330165Seadlerstatic struct iwm_nvm_data * 332330165Seadler iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, 333330165Seadler const uint16_t *, const uint16_t *, 334330165Seadler const uint16_t *, const uint16_t *, 335330165Seadler const uint16_t *); 336330165Seadlerstatic void iwm_free_nvm_data(struct iwm_nvm_data *); 337330165Seadlerstatic void iwm_set_hw_address_family_8000(struct iwm_softc *, 338330165Seadler struct iwm_nvm_data *, 339330165Seadler const uint16_t *, 340330165Seadler const uint16_t *); 341303628Ssbrunostatic int iwm_get_sku(const struct iwm_softc *, const uint16_t *, 342303628Ssbruno const uint16_t *); 343303628Ssbrunostatic int iwm_get_nvm_version(const struct iwm_softc *, const uint16_t *); 344303628Ssbrunostatic int iwm_get_radio_cfg(const struct iwm_softc *, const uint16_t *, 345303628Ssbruno const uint16_t *); 346303628Ssbrunostatic int iwm_get_n_hw_addrs(const struct iwm_softc *, 347303628Ssbruno const uint16_t *); 348303628Ssbrunostatic void iwm_set_radio_cfg(const struct iwm_softc *, 349303628Ssbruno struct iwm_nvm_data *, uint32_t); 350330165Seadlerstatic struct iwm_nvm_data * 351330165Seadler iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); 352286441Srpaulostatic int iwm_nvm_init(struct iwm_softc *); 353303628Ssbrunostatic int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, 354303628Ssbruno const uint8_t *, uint32_t); 355286441Srpaulostatic int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, 356286441Srpaulo const uint8_t *, uint32_t); 357303628Ssbrunostatic int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); 358303628Ssbrunostatic int iwm_load_cpu_sections_8000(struct iwm_softc *, 359303628Ssbruno struct iwm_fw_sects *, int , int *); 360303628Ssbrunostatic int iwm_load_firmware_8000(struct iwm_softc *, enum iwm_ucode_type); 361286441Srpaulostatic int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); 362286441Srpaulostatic int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); 363286441Srpaulostatic int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); 364286441Srpaulostatic int iwm_send_phy_cfg_cmd(struct iwm_softc *); 365286441Srpaulostatic int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, 366286441Srpaulo enum iwm_ucode_type); 367286441Srpaulostatic int iwm_run_init_mvm_ucode(struct iwm_softc *, int); 368286441Srpaulostatic int iwm_rx_addbuf(struct iwm_softc *, int, int); 369286441Srpaulostatic int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); 370286441Srpaulostatic int iwm_mvm_get_signal_strength(struct iwm_softc *, 371286441Srpaulo struct iwm_rx_phy_info *); 372286441Srpaulostatic void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, 373286441Srpaulo struct iwm_rx_packet *, 374286441Srpaulo struct iwm_rx_data *); 375330144Seadlerstatic int iwm_get_noise(struct iwm_softc *sc, 376330144Seadler const struct iwm_mvm_statistics_rx_non_phy *); 377286441Srpaulostatic void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, 378286441Srpaulo struct iwm_rx_data *); 379293100Savosstatic int iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, 380286441Srpaulo struct iwm_rx_packet *, 381286441Srpaulo struct iwm_node *); 382286441Srpaulostatic void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, 383286441Srpaulo struct iwm_rx_data *); 384286441Srpaulostatic void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); 385286441Srpaulo#if 0 386286441Srpaulostatic void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, 387286441Srpaulo uint16_t); 388286441Srpaulo#endif 389286441Srpaulostatic const struct iwm_rate * 390286441Srpaulo iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, 391330155Seadler struct mbuf *, struct iwm_tx_cmd *); 392286441Srpaulostatic int iwm_tx(struct iwm_softc *, struct mbuf *, 393286441Srpaulo struct ieee80211_node *, int); 394286441Srpaulostatic int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, 395286441Srpaulo const struct ieee80211_bpf_params *); 396330154Seadlerstatic int iwm_mvm_flush_tx_path(struct iwm_softc *sc, 397330154Seadler uint32_t tfd_msk, uint32_t flags); 398286441Srpaulostatic int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, 399303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 *, 400286441Srpaulo int *); 401286441Srpaulostatic int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, 402286441Srpaulo int); 403286441Srpaulostatic int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); 404286441Srpaulostatic int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); 405286441Srpaulostatic int iwm_mvm_add_int_sta_common(struct iwm_softc *, 406286441Srpaulo struct iwm_int_sta *, 407286441Srpaulo const uint8_t *, uint16_t, uint16_t); 408286441Srpaulostatic int iwm_mvm_add_aux_sta(struct iwm_softc *); 409286441Srpaulostatic int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *); 410286441Srpaulostatic int iwm_auth(struct ieee80211vap *, struct iwm_softc *); 411286441Srpaulostatic int iwm_assoc(struct ieee80211vap *, struct iwm_softc *); 412286441Srpaulostatic int iwm_release(struct iwm_softc *, struct iwm_node *); 413286441Srpaulostatic struct ieee80211_node * 414286441Srpaulo iwm_node_alloc(struct ieee80211vap *, 415286441Srpaulo const uint8_t[IEEE80211_ADDR_LEN]); 416286441Srpaulostatic void iwm_setrates(struct iwm_softc *, struct iwm_node *); 417286441Srpaulostatic int iwm_media_change(struct ifnet *); 418286441Srpaulostatic int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); 419286441Srpaulostatic void iwm_endscan_cb(void *, int); 420303628Ssbrunostatic void iwm_mvm_fill_sf_command(struct iwm_softc *, 421303628Ssbruno struct iwm_sf_cfg_cmd *, 422303628Ssbruno struct ieee80211_node *); 423303628Ssbrunostatic int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); 424303628Ssbrunostatic int iwm_send_bt_init_conf(struct iwm_softc *); 425303628Ssbrunostatic int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); 426303628Ssbrunostatic void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); 427286441Srpaulostatic int iwm_init_hw(struct iwm_softc *); 428287197Sglebiusstatic void iwm_init(struct iwm_softc *); 429287197Sglebiusstatic void iwm_start(struct iwm_softc *); 430287197Sglebiusstatic void iwm_stop(struct iwm_softc *); 431286441Srpaulostatic void iwm_watchdog(void *); 432287197Sglebiusstatic void iwm_parent(struct ieee80211com *); 433286441Srpaulo#ifdef IWM_DEBUG 434286441Srpaulostatic const char * 435286441Srpaulo iwm_desc_lookup(uint32_t); 436286441Srpaulostatic void iwm_nic_error(struct iwm_softc *); 437303628Ssbrunostatic void iwm_nic_umac_error(struct iwm_softc *); 438286441Srpaulo#endif 439286441Srpaulostatic void iwm_notif_intr(struct iwm_softc *); 440286441Srpaulostatic void iwm_intr(void *); 441286441Srpaulostatic int iwm_attach(device_t); 442303628Ssbrunostatic int iwm_is_valid_ether_addr(uint8_t *); 443286441Srpaulostatic void iwm_preinit(void *); 444286441Srpaulostatic int iwm_detach_local(struct iwm_softc *sc, int); 445286441Srpaulostatic void iwm_init_task(void *); 446286441Srpaulostatic void iwm_radiotap_attach(struct iwm_softc *); 447286441Srpaulostatic struct ieee80211vap * 448286441Srpaulo iwm_vap_create(struct ieee80211com *, 449286441Srpaulo const char [IFNAMSIZ], int, 450286441Srpaulo enum ieee80211_opmode, int, 451286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 452286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN]); 453286441Srpaulostatic void iwm_vap_delete(struct ieee80211vap *); 454286441Srpaulostatic void iwm_scan_start(struct ieee80211com *); 455286441Srpaulostatic void iwm_scan_end(struct ieee80211com *); 456286441Srpaulostatic void iwm_update_mcast(struct ieee80211com *); 457286441Srpaulostatic void iwm_set_channel(struct ieee80211com *); 458286441Srpaulostatic void iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long); 459286441Srpaulostatic void iwm_scan_mindwell(struct ieee80211_scan_state *); 460286441Srpaulostatic int iwm_detach(device_t); 461286441Srpaulo 462286441Srpaulo/* 463286441Srpaulo * Firmware parser. 464286441Srpaulo */ 465286441Srpaulo 466286441Srpaulostatic int 467286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen) 468286441Srpaulo{ 469286441Srpaulo const struct iwm_fw_cscheme_list *l = (const void *)data; 470286441Srpaulo 471286441Srpaulo if (dlen < sizeof(*l) || 472286441Srpaulo dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) 473286441Srpaulo return EINVAL; 474286441Srpaulo 475286441Srpaulo /* we don't actually store anything for now, always use s/w crypto */ 476286441Srpaulo 477286441Srpaulo return 0; 478286441Srpaulo} 479286441Srpaulo 480286441Srpaulostatic int 481286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc, 482286441Srpaulo enum iwm_ucode_type type, const uint8_t *data, size_t dlen) 483286441Srpaulo{ 484286441Srpaulo struct iwm_fw_sects *fws; 485286441Srpaulo struct iwm_fw_onesect *fwone; 486286441Srpaulo 487286441Srpaulo if (type >= IWM_UCODE_TYPE_MAX) 488286441Srpaulo return EINVAL; 489286441Srpaulo if (dlen < sizeof(uint32_t)) 490286441Srpaulo return EINVAL; 491286441Srpaulo 492286441Srpaulo fws = &sc->sc_fw.fw_sects[type]; 493286441Srpaulo if (fws->fw_count >= IWM_UCODE_SECT_MAX) 494286441Srpaulo return EINVAL; 495286441Srpaulo 496286441Srpaulo fwone = &fws->fw_sect[fws->fw_count]; 497286441Srpaulo 498286441Srpaulo /* first 32bit are device load offset */ 499286441Srpaulo memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); 500286441Srpaulo 501286441Srpaulo /* rest is data */ 502286441Srpaulo fwone->fws_data = data + sizeof(uint32_t); 503286441Srpaulo fwone->fws_len = dlen - sizeof(uint32_t); 504286441Srpaulo 505286441Srpaulo fws->fw_count++; 506286441Srpaulo 507286441Srpaulo return 0; 508286441Srpaulo} 509286441Srpaulo 510330152Seadler#define IWM_DEFAULT_SCAN_CHANNELS 40 511330152Seadler 512286441Srpaulo/* iwlwifi: iwl-drv.c */ 513286441Srpaulostruct iwm_tlv_calib_data { 514286441Srpaulo uint32_t ucode_type; 515286441Srpaulo struct iwm_tlv_calib_ctrl calib; 516286441Srpaulo} __packed; 517286441Srpaulo 518286441Srpaulostatic int 519286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data) 520286441Srpaulo{ 521286441Srpaulo const struct iwm_tlv_calib_data *def_calib = data; 522286441Srpaulo uint32_t ucode_type = le32toh(def_calib->ucode_type); 523286441Srpaulo 524286441Srpaulo if (ucode_type >= IWM_UCODE_TYPE_MAX) { 525286441Srpaulo device_printf(sc->sc_dev, 526286441Srpaulo "Wrong ucode_type %u for default " 527286441Srpaulo "calibration.\n", ucode_type); 528286441Srpaulo return EINVAL; 529286441Srpaulo } 530286441Srpaulo 531286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger = 532286441Srpaulo def_calib->calib.flow_trigger; 533286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger = 534286441Srpaulo def_calib->calib.event_trigger; 535286441Srpaulo 536286441Srpaulo return 0; 537286441Srpaulo} 538286441Srpaulo 539286441Srpaulostatic void 540286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw) 541286441Srpaulo{ 542293177Savos firmware_put(fw->fw_fp, FIRMWARE_UNLOAD); 543293177Savos fw->fw_fp = NULL; 544286441Srpaulo /* don't touch fw->fw_status */ 545286441Srpaulo memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); 546286441Srpaulo} 547286441Srpaulo 548286441Srpaulostatic int 549286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 550286441Srpaulo{ 551286441Srpaulo struct iwm_fw_info *fw = &sc->sc_fw; 552286441Srpaulo const struct iwm_tlv_ucode_header *uhdr; 553286441Srpaulo struct iwm_ucode_tlv tlv; 554286441Srpaulo enum iwm_ucode_tlv_type tlv_type; 555286441Srpaulo const struct firmware *fwp; 556286441Srpaulo const uint8_t *data; 557286441Srpaulo int error = 0; 558286441Srpaulo size_t len; 559286441Srpaulo 560286441Srpaulo if (fw->fw_status == IWM_FW_STATUS_DONE && 561286441Srpaulo ucode_type != IWM_UCODE_TYPE_INIT) 562286441Srpaulo return 0; 563286441Srpaulo 564286441Srpaulo while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) 565286441Srpaulo msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); 566286441Srpaulo fw->fw_status = IWM_FW_STATUS_INPROGRESS; 567286441Srpaulo 568293177Savos if (fw->fw_fp != NULL) 569286441Srpaulo iwm_fw_info_free(fw); 570286441Srpaulo 571286441Srpaulo /* 572286441Srpaulo * Load firmware into driver memory. 573293177Savos * fw_fp will be set. 574286441Srpaulo */ 575286441Srpaulo IWM_UNLOCK(sc); 576330166Seadler fwp = firmware_get(sc->cfg->fw_name); 577293177Savos IWM_LOCK(sc); 578286441Srpaulo if (fwp == NULL) { 579286441Srpaulo device_printf(sc->sc_dev, 580286441Srpaulo "could not read firmware %s (error %d)\n", 581330166Seadler sc->cfg->fw_name, error); 582286441Srpaulo goto out; 583286441Srpaulo } 584293177Savos fw->fw_fp = fwp; 585286441Srpaulo 586303628Ssbruno /* (Re-)Initialize default values. */ 587303628Ssbruno sc->sc_capaflags = 0; 588330152Seadler sc->sc_capa_n_scan_channels = IWM_DEFAULT_SCAN_CHANNELS; 589303628Ssbruno memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); 590303628Ssbruno memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); 591303628Ssbruno 592286441Srpaulo /* 593286441Srpaulo * Parse firmware contents 594286441Srpaulo */ 595286441Srpaulo 596293177Savos uhdr = (const void *)fw->fw_fp->data; 597293177Savos if (*(const uint32_t *)fw->fw_fp->data != 0 598286441Srpaulo || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { 599286441Srpaulo device_printf(sc->sc_dev, "invalid firmware %s\n", 600330166Seadler sc->cfg->fw_name); 601286441Srpaulo error = EINVAL; 602286441Srpaulo goto out; 603286441Srpaulo } 604286441Srpaulo 605303628Ssbruno snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", 606303628Ssbruno IWM_UCODE_MAJOR(le32toh(uhdr->ver)), 607303628Ssbruno IWM_UCODE_MINOR(le32toh(uhdr->ver)), 608303628Ssbruno IWM_UCODE_API(le32toh(uhdr->ver))); 609286441Srpaulo data = uhdr->data; 610293177Savos len = fw->fw_fp->datasize - sizeof(*uhdr); 611286441Srpaulo 612286441Srpaulo while (len >= sizeof(tlv)) { 613286441Srpaulo size_t tlv_len; 614286441Srpaulo const void *tlv_data; 615286441Srpaulo 616286441Srpaulo memcpy(&tlv, data, sizeof(tlv)); 617286441Srpaulo tlv_len = le32toh(tlv.length); 618286441Srpaulo tlv_type = le32toh(tlv.type); 619286441Srpaulo 620286441Srpaulo len -= sizeof(tlv); 621286441Srpaulo data += sizeof(tlv); 622286441Srpaulo tlv_data = data; 623286441Srpaulo 624286441Srpaulo if (len < tlv_len) { 625286441Srpaulo device_printf(sc->sc_dev, 626286441Srpaulo "firmware too short: %zu bytes\n", 627286441Srpaulo len); 628286441Srpaulo error = EINVAL; 629286441Srpaulo goto parse_out; 630286441Srpaulo } 631286441Srpaulo 632286441Srpaulo switch ((int)tlv_type) { 633286441Srpaulo case IWM_UCODE_TLV_PROBE_MAX_LEN: 634286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 635286441Srpaulo device_printf(sc->sc_dev, 636286441Srpaulo "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n", 637286441Srpaulo __func__, 638286441Srpaulo (int) tlv_len); 639286441Srpaulo error = EINVAL; 640286441Srpaulo goto parse_out; 641286441Srpaulo } 642286441Srpaulo sc->sc_capa_max_probe_len 643286441Srpaulo = le32toh(*(const uint32_t *)tlv_data); 644286441Srpaulo /* limit it to something sensible */ 645303628Ssbruno if (sc->sc_capa_max_probe_len > 646303628Ssbruno IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { 647286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 648286441Srpaulo "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " 649286441Srpaulo "ridiculous\n", __func__); 650286441Srpaulo error = EINVAL; 651286441Srpaulo goto parse_out; 652286441Srpaulo } 653286441Srpaulo break; 654286441Srpaulo case IWM_UCODE_TLV_PAN: 655286441Srpaulo if (tlv_len) { 656286441Srpaulo device_printf(sc->sc_dev, 657286441Srpaulo "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", 658286441Srpaulo __func__, 659286441Srpaulo (int) tlv_len); 660286441Srpaulo error = EINVAL; 661286441Srpaulo goto parse_out; 662286441Srpaulo } 663286441Srpaulo sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; 664286441Srpaulo break; 665286441Srpaulo case IWM_UCODE_TLV_FLAGS: 666286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 667286441Srpaulo device_printf(sc->sc_dev, 668286441Srpaulo "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", 669286441Srpaulo __func__, 670286441Srpaulo (int) tlv_len); 671286441Srpaulo error = EINVAL; 672286441Srpaulo goto parse_out; 673286441Srpaulo } 674286441Srpaulo /* 675286441Srpaulo * Apparently there can be many flags, but Linux driver 676286441Srpaulo * parses only the first one, and so do we. 677286441Srpaulo * 678286441Srpaulo * XXX: why does this override IWM_UCODE_TLV_PAN? 679286441Srpaulo * Intentional or a bug? Observations from 680286441Srpaulo * current firmware file: 681286441Srpaulo * 1) TLV_PAN is parsed first 682286441Srpaulo * 2) TLV_FLAGS contains TLV_FLAGS_PAN 683286441Srpaulo * ==> this resets TLV_PAN to itself... hnnnk 684286441Srpaulo */ 685286441Srpaulo sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data); 686286441Srpaulo break; 687286441Srpaulo case IWM_UCODE_TLV_CSCHEME: 688286441Srpaulo if ((error = iwm_store_cscheme(sc, 689286441Srpaulo tlv_data, tlv_len)) != 0) { 690286441Srpaulo device_printf(sc->sc_dev, 691286441Srpaulo "%s: iwm_store_cscheme(): returned %d\n", 692286441Srpaulo __func__, 693286441Srpaulo error); 694286441Srpaulo goto parse_out; 695286441Srpaulo } 696286441Srpaulo break; 697303628Ssbruno case IWM_UCODE_TLV_NUM_OF_CPU: { 698303628Ssbruno uint32_t num_cpu; 699286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 700286441Srpaulo device_printf(sc->sc_dev, 701286441Srpaulo "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", 702286441Srpaulo __func__, 703286441Srpaulo (int) tlv_len); 704286441Srpaulo error = EINVAL; 705286441Srpaulo goto parse_out; 706286441Srpaulo } 707303628Ssbruno num_cpu = le32toh(*(const uint32_t *)tlv_data); 708303628Ssbruno if (num_cpu < 1 || num_cpu > 2) { 709286441Srpaulo device_printf(sc->sc_dev, 710303628Ssbruno "%s: Driver supports only 1 or 2 CPUs\n", 711286441Srpaulo __func__); 712286441Srpaulo error = EINVAL; 713286441Srpaulo goto parse_out; 714286441Srpaulo } 715286441Srpaulo break; 716303628Ssbruno } 717286441Srpaulo case IWM_UCODE_TLV_SEC_RT: 718286441Srpaulo if ((error = iwm_firmware_store_section(sc, 719286441Srpaulo IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) { 720286441Srpaulo device_printf(sc->sc_dev, 721286441Srpaulo "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n", 722286441Srpaulo __func__, 723286441Srpaulo error); 724286441Srpaulo goto parse_out; 725286441Srpaulo } 726286441Srpaulo break; 727286441Srpaulo case IWM_UCODE_TLV_SEC_INIT: 728286441Srpaulo if ((error = iwm_firmware_store_section(sc, 729286441Srpaulo IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) { 730286441Srpaulo device_printf(sc->sc_dev, 731286441Srpaulo "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n", 732286441Srpaulo __func__, 733286441Srpaulo error); 734286441Srpaulo goto parse_out; 735286441Srpaulo } 736286441Srpaulo break; 737286441Srpaulo case IWM_UCODE_TLV_SEC_WOWLAN: 738286441Srpaulo if ((error = iwm_firmware_store_section(sc, 739286441Srpaulo IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) { 740286441Srpaulo device_printf(sc->sc_dev, 741286441Srpaulo "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n", 742286441Srpaulo __func__, 743286441Srpaulo error); 744286441Srpaulo goto parse_out; 745286441Srpaulo } 746286441Srpaulo break; 747286441Srpaulo case IWM_UCODE_TLV_DEF_CALIB: 748286441Srpaulo if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { 749286441Srpaulo device_printf(sc->sc_dev, 750286441Srpaulo "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", 751286441Srpaulo __func__, 752286441Srpaulo (int) tlv_len, 753286441Srpaulo (int) sizeof(struct iwm_tlv_calib_data)); 754286441Srpaulo error = EINVAL; 755286441Srpaulo goto parse_out; 756286441Srpaulo } 757286441Srpaulo if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { 758286441Srpaulo device_printf(sc->sc_dev, 759286441Srpaulo "%s: iwm_set_default_calib() failed: %d\n", 760286441Srpaulo __func__, 761286441Srpaulo error); 762286441Srpaulo goto parse_out; 763286441Srpaulo } 764286441Srpaulo break; 765286441Srpaulo case IWM_UCODE_TLV_PHY_SKU: 766286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 767286441Srpaulo error = EINVAL; 768286441Srpaulo device_printf(sc->sc_dev, 769286441Srpaulo "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", 770286441Srpaulo __func__, 771286441Srpaulo (int) tlv_len); 772286441Srpaulo goto parse_out; 773286441Srpaulo } 774286441Srpaulo sc->sc_fw_phy_config = 775286441Srpaulo le32toh(*(const uint32_t *)tlv_data); 776286441Srpaulo break; 777286441Srpaulo 778303628Ssbruno case IWM_UCODE_TLV_API_CHANGES_SET: { 779303628Ssbruno const struct iwm_ucode_api *api; 780303628Ssbruno if (tlv_len != sizeof(*api)) { 781303628Ssbruno error = EINVAL; 782303628Ssbruno goto parse_out; 783303628Ssbruno } 784303628Ssbruno api = (const struct iwm_ucode_api *)tlv_data; 785303628Ssbruno /* Flags may exceed 32 bits in future firmware. */ 786303628Ssbruno if (le32toh(api->api_index) > 0) { 787303628Ssbruno device_printf(sc->sc_dev, 788303628Ssbruno "unsupported API index %d\n", 789303628Ssbruno le32toh(api->api_index)); 790303628Ssbruno goto parse_out; 791303628Ssbruno } 792303628Ssbruno sc->sc_ucode_api = le32toh(api->api_flags); 793303628Ssbruno break; 794303628Ssbruno } 795303628Ssbruno 796303628Ssbruno case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { 797303628Ssbruno const struct iwm_ucode_capa *capa; 798303628Ssbruno int idx, i; 799303628Ssbruno if (tlv_len != sizeof(*capa)) { 800303628Ssbruno error = EINVAL; 801303628Ssbruno goto parse_out; 802303628Ssbruno } 803303628Ssbruno capa = (const struct iwm_ucode_capa *)tlv_data; 804303628Ssbruno idx = le32toh(capa->api_index); 805330148Seadler if (idx >= howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { 806303628Ssbruno device_printf(sc->sc_dev, 807303628Ssbruno "unsupported API index %d\n", idx); 808303628Ssbruno goto parse_out; 809303628Ssbruno } 810303628Ssbruno for (i = 0; i < 32; i++) { 811303628Ssbruno if ((le32toh(capa->api_capa) & (1U << i)) == 0) 812303628Ssbruno continue; 813303628Ssbruno setbit(sc->sc_enabled_capa, i + (32 * idx)); 814303628Ssbruno } 815303628Ssbruno break; 816303628Ssbruno } 817303628Ssbruno 818303628Ssbruno case 48: /* undocumented TLV */ 819303628Ssbruno case IWM_UCODE_TLV_SDIO_ADMA_ADDR: 820303628Ssbruno case IWM_UCODE_TLV_FW_GSCAN_CAPA: 821286441Srpaulo /* ignore, not used by current driver */ 822286441Srpaulo break; 823286441Srpaulo 824303628Ssbruno case IWM_UCODE_TLV_SEC_RT_USNIFFER: 825303628Ssbruno if ((error = iwm_firmware_store_section(sc, 826303628Ssbruno IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, 827303628Ssbruno tlv_len)) != 0) 828303628Ssbruno goto parse_out; 829303628Ssbruno break; 830303628Ssbruno 831303628Ssbruno case IWM_UCODE_TLV_N_SCAN_CHANNELS: 832303628Ssbruno if (tlv_len != sizeof(uint32_t)) { 833303628Ssbruno error = EINVAL; 834303628Ssbruno goto parse_out; 835303628Ssbruno } 836303628Ssbruno sc->sc_capa_n_scan_channels = 837303628Ssbruno le32toh(*(const uint32_t *)tlv_data); 838303628Ssbruno break; 839303628Ssbruno 840303628Ssbruno case IWM_UCODE_TLV_FW_VERSION: 841303628Ssbruno if (tlv_len != sizeof(uint32_t) * 3) { 842303628Ssbruno error = EINVAL; 843303628Ssbruno goto parse_out; 844303628Ssbruno } 845303628Ssbruno snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), 846303628Ssbruno "%d.%d.%d", 847303628Ssbruno le32toh(((const uint32_t *)tlv_data)[0]), 848303628Ssbruno le32toh(((const uint32_t *)tlv_data)[1]), 849303628Ssbruno le32toh(((const uint32_t *)tlv_data)[2])); 850303628Ssbruno break; 851303628Ssbruno 852286441Srpaulo default: 853286441Srpaulo device_printf(sc->sc_dev, 854286441Srpaulo "%s: unknown firmware section %d, abort\n", 855286441Srpaulo __func__, tlv_type); 856286441Srpaulo error = EINVAL; 857286441Srpaulo goto parse_out; 858286441Srpaulo } 859286441Srpaulo 860286441Srpaulo len -= roundup(tlv_len, 4); 861286441Srpaulo data += roundup(tlv_len, 4); 862286441Srpaulo } 863286441Srpaulo 864286441Srpaulo KASSERT(error == 0, ("unhandled error")); 865286441Srpaulo 866286441Srpaulo parse_out: 867286441Srpaulo if (error) { 868286441Srpaulo device_printf(sc->sc_dev, "firmware parse error %d, " 869286441Srpaulo "section type %d\n", error, tlv_type); 870286441Srpaulo } 871286441Srpaulo 872286441Srpaulo if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { 873286441Srpaulo device_printf(sc->sc_dev, 874286441Srpaulo "device uses unsupported power ops\n"); 875286441Srpaulo error = ENOTSUP; 876286441Srpaulo } 877286441Srpaulo 878286441Srpaulo out: 879286441Srpaulo if (error) { 880286441Srpaulo fw->fw_status = IWM_FW_STATUS_NONE; 881293177Savos if (fw->fw_fp != NULL) 882286441Srpaulo iwm_fw_info_free(fw); 883286441Srpaulo } else 884286441Srpaulo fw->fw_status = IWM_FW_STATUS_DONE; 885286441Srpaulo wakeup(&sc->sc_fw); 886286441Srpaulo 887286441Srpaulo return error; 888286441Srpaulo} 889286441Srpaulo 890286441Srpaulo/* 891286441Srpaulo * DMA resource routines 892286441Srpaulo */ 893286441Srpaulo 894286441Srpaulostatic void 895286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 896286441Srpaulo{ 897286441Srpaulo if (error != 0) 898286441Srpaulo return; 899286441Srpaulo KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 900303628Ssbruno *(bus_addr_t *)arg = segs[0].ds_addr; 901286441Srpaulo} 902286441Srpaulo 903286441Srpaulostatic int 904286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, 905286441Srpaulo bus_size_t size, bus_size_t alignment) 906286441Srpaulo{ 907286441Srpaulo int error; 908286441Srpaulo 909286441Srpaulo dma->tag = NULL; 910303628Ssbruno dma->map = NULL; 911286441Srpaulo dma->size = size; 912302102Sadrian dma->vaddr = NULL; 913286441Srpaulo 914286441Srpaulo error = bus_dma_tag_create(tag, alignment, 915286441Srpaulo 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 916289679Skevlo 1, size, 0, NULL, NULL, &dma->tag); 917286441Srpaulo if (error != 0) 918286441Srpaulo goto fail; 919286441Srpaulo 920286441Srpaulo error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, 921286441Srpaulo BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); 922286441Srpaulo if (error != 0) 923286441Srpaulo goto fail; 924286441Srpaulo 925286441Srpaulo error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, 926286441Srpaulo iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); 927302102Sadrian if (error != 0) { 928302102Sadrian bus_dmamem_free(dma->tag, dma->vaddr, dma->map); 929302102Sadrian dma->vaddr = NULL; 930303628Ssbruno goto fail; 931302102Sadrian } 932286441Srpaulo 933286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 934286441Srpaulo 935286441Srpaulo return 0; 936286441Srpaulo 937303628Ssbrunofail: 938303628Ssbruno iwm_dma_contig_free(dma); 939303628Ssbruno 940286441Srpaulo return error; 941286441Srpaulo} 942286441Srpaulo 943286441Srpaulostatic void 944286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma) 945286441Srpaulo{ 946302102Sadrian if (dma->vaddr != NULL) { 947302102Sadrian bus_dmamap_sync(dma->tag, dma->map, 948302102Sadrian BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 949302102Sadrian bus_dmamap_unload(dma->tag, dma->map); 950302102Sadrian bus_dmamem_free(dma->tag, dma->vaddr, dma->map); 951302102Sadrian dma->vaddr = NULL; 952286441Srpaulo } 953286441Srpaulo if (dma->tag != NULL) { 954286441Srpaulo bus_dma_tag_destroy(dma->tag); 955286441Srpaulo dma->tag = NULL; 956286441Srpaulo } 957286441Srpaulo} 958286441Srpaulo 959286441Srpaulo/* fwmem is used to load firmware onto the card */ 960286441Srpaulostatic int 961286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc) 962286441Srpaulo{ 963286441Srpaulo /* Must be aligned on a 16-byte boundary. */ 964286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, 965286441Srpaulo sc->sc_fwdmasegsz, 16); 966286441Srpaulo} 967286441Srpaulo 968286441Srpaulo/* tx scheduler rings. not used? */ 969286441Srpaulostatic int 970286441Srpauloiwm_alloc_sched(struct iwm_softc *sc) 971286441Srpaulo{ 972286441Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 973303628Ssbruno return iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, 974286441Srpaulo nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); 975286441Srpaulo} 976286441Srpaulo 977286441Srpaulo/* keep-warm page is used internally by the card. see iwl-fh.h for more info */ 978286441Srpaulostatic int 979286441Srpauloiwm_alloc_kw(struct iwm_softc *sc) 980286441Srpaulo{ 981286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); 982286441Srpaulo} 983286441Srpaulo 984286441Srpaulo/* interrupt cause table */ 985286441Srpaulostatic int 986286441Srpauloiwm_alloc_ict(struct iwm_softc *sc) 987286441Srpaulo{ 988286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, 989286441Srpaulo IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); 990286441Srpaulo} 991286441Srpaulo 992286441Srpaulostatic int 993286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 994286441Srpaulo{ 995286441Srpaulo bus_size_t size; 996286441Srpaulo int i, error; 997286441Srpaulo 998286441Srpaulo ring->cur = 0; 999286441Srpaulo 1000286441Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 1001286441Srpaulo size = IWM_RX_RING_COUNT * sizeof(uint32_t); 1002286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 1003286441Srpaulo if (error != 0) { 1004286441Srpaulo device_printf(sc->sc_dev, 1005286441Srpaulo "could not allocate RX ring DMA memory\n"); 1006286441Srpaulo goto fail; 1007286441Srpaulo } 1008286441Srpaulo ring->desc = ring->desc_dma.vaddr; 1009286441Srpaulo 1010286441Srpaulo /* Allocate RX status area (16-byte aligned). */ 1011286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, 1012286441Srpaulo sizeof(*ring->stat), 16); 1013286441Srpaulo if (error != 0) { 1014286441Srpaulo device_printf(sc->sc_dev, 1015286441Srpaulo "could not allocate RX status DMA memory\n"); 1016286441Srpaulo goto fail; 1017286441Srpaulo } 1018286441Srpaulo ring->stat = ring->stat_dma.vaddr; 1019286441Srpaulo 1020286441Srpaulo /* Create RX buffer DMA tag. */ 1021286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 1022286441Srpaulo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 1023289679Skevlo IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, 0, NULL, NULL, &ring->data_dmat); 1024286441Srpaulo if (error != 0) { 1025286441Srpaulo device_printf(sc->sc_dev, 1026286441Srpaulo "%s: could not create RX buf DMA tag, error %d\n", 1027286441Srpaulo __func__, error); 1028286441Srpaulo goto fail; 1029286441Srpaulo } 1030286441Srpaulo 1031301845Sadrian /* Allocate spare bus_dmamap_t for iwm_rx_addbuf() */ 1032301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &ring->spare_map); 1033301845Sadrian if (error != 0) { 1034301845Sadrian device_printf(sc->sc_dev, 1035301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 1036301845Sadrian __func__, error); 1037301845Sadrian goto fail; 1038301845Sadrian } 1039286441Srpaulo /* 1040286441Srpaulo * Allocate and map RX buffers. 1041286441Srpaulo */ 1042286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 1043301845Sadrian struct iwm_rx_data *data = &ring->data[i]; 1044301845Sadrian error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1045301845Sadrian if (error != 0) { 1046301845Sadrian device_printf(sc->sc_dev, 1047301845Sadrian "%s: could not create RX buf DMA map, error %d\n", 1048301845Sadrian __func__, error); 1049301845Sadrian goto fail; 1050301845Sadrian } 1051301845Sadrian data->m = NULL; 1052301845Sadrian 1053286441Srpaulo if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) { 1054286441Srpaulo goto fail; 1055286441Srpaulo } 1056286441Srpaulo } 1057286441Srpaulo return 0; 1058286441Srpaulo 1059286441Srpaulofail: iwm_free_rx_ring(sc, ring); 1060286441Srpaulo return error; 1061286441Srpaulo} 1062286441Srpaulo 1063286441Srpaulostatic void 1064301191Sadrianiwm_disable_rx_dma(struct iwm_softc *sc) 1065286441Srpaulo{ 1066303628Ssbruno /* XXX conditional nic locks are stupid */ 1067286441Srpaulo /* XXX print out if we can't lock the NIC? */ 1068286441Srpaulo if (iwm_nic_lock(sc)) { 1069286441Srpaulo /* XXX handle if RX stop doesn't finish? */ 1070286441Srpaulo (void) iwm_pcie_rx_stop(sc); 1071286441Srpaulo iwm_nic_unlock(sc); 1072286441Srpaulo } 1073301191Sadrian} 1074301191Sadrian 1075301191Sadrianstatic void 1076301191Sadrianiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1077301191Sadrian{ 1078287965Sadrian /* Reset the ring state */ 1079286441Srpaulo ring->cur = 0; 1080303628Ssbruno 1081303628Ssbruno /* 1082303628Ssbruno * The hw rx ring index in shared memory must also be cleared, 1083303628Ssbruno * otherwise the discrepancy can cause reprocessing chaos. 1084303628Ssbruno */ 1085287965Sadrian memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1086286441Srpaulo} 1087286441Srpaulo 1088286441Srpaulostatic void 1089286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 1090286441Srpaulo{ 1091286441Srpaulo int i; 1092286441Srpaulo 1093286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1094286441Srpaulo iwm_dma_contig_free(&ring->stat_dma); 1095286441Srpaulo 1096286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 1097286441Srpaulo struct iwm_rx_data *data = &ring->data[i]; 1098286441Srpaulo 1099286441Srpaulo if (data->m != NULL) { 1100286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1101286441Srpaulo BUS_DMASYNC_POSTREAD); 1102286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1103286441Srpaulo m_freem(data->m); 1104286441Srpaulo data->m = NULL; 1105286441Srpaulo } 1106286441Srpaulo if (data->map != NULL) { 1107286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1108286441Srpaulo data->map = NULL; 1109286441Srpaulo } 1110286441Srpaulo } 1111301845Sadrian if (ring->spare_map != NULL) { 1112301845Sadrian bus_dmamap_destroy(ring->data_dmat, ring->spare_map); 1113301845Sadrian ring->spare_map = NULL; 1114301845Sadrian } 1115286441Srpaulo if (ring->data_dmat != NULL) { 1116286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1117286441Srpaulo ring->data_dmat = NULL; 1118286441Srpaulo } 1119286441Srpaulo} 1120286441Srpaulo 1121286441Srpaulostatic int 1122286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) 1123286441Srpaulo{ 1124286441Srpaulo bus_addr_t paddr; 1125286441Srpaulo bus_size_t size; 1126302104Sadrian size_t maxsize; 1127302104Sadrian int nsegments; 1128286441Srpaulo int i, error; 1129286441Srpaulo 1130286441Srpaulo ring->qid = qid; 1131286441Srpaulo ring->queued = 0; 1132286441Srpaulo ring->cur = 0; 1133286441Srpaulo 1134286441Srpaulo /* Allocate TX descriptors (256-byte aligned). */ 1135286441Srpaulo size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd); 1136286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 1137286441Srpaulo if (error != 0) { 1138286441Srpaulo device_printf(sc->sc_dev, 1139286441Srpaulo "could not allocate TX ring DMA memory\n"); 1140286441Srpaulo goto fail; 1141286441Srpaulo } 1142286441Srpaulo ring->desc = ring->desc_dma.vaddr; 1143286441Srpaulo 1144286441Srpaulo /* 1145286441Srpaulo * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need 1146286441Srpaulo * to allocate commands space for other rings. 1147286441Srpaulo */ 1148286441Srpaulo if (qid > IWM_MVM_CMD_QUEUE) 1149286441Srpaulo return 0; 1150286441Srpaulo 1151286441Srpaulo size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd); 1152286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4); 1153286441Srpaulo if (error != 0) { 1154286441Srpaulo device_printf(sc->sc_dev, 1155286441Srpaulo "could not allocate TX cmd DMA memory\n"); 1156286441Srpaulo goto fail; 1157286441Srpaulo } 1158286441Srpaulo ring->cmd = ring->cmd_dma.vaddr; 1159286441Srpaulo 1160302104Sadrian /* FW commands may require more mapped space than packets. */ 1161302104Sadrian if (qid == IWM_MVM_CMD_QUEUE) { 1162302104Sadrian maxsize = IWM_RBUF_SIZE; 1163302104Sadrian nsegments = 1; 1164302104Sadrian } else { 1165302104Sadrian maxsize = MCLBYTES; 1166302104Sadrian nsegments = IWM_MAX_SCATTER - 2; 1167302104Sadrian } 1168302104Sadrian 1169286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 1170302104Sadrian BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, 1171302104Sadrian nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat); 1172286441Srpaulo if (error != 0) { 1173286441Srpaulo device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); 1174286441Srpaulo goto fail; 1175286441Srpaulo } 1176286441Srpaulo 1177286441Srpaulo paddr = ring->cmd_dma.paddr; 1178286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1179286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1180286441Srpaulo 1181286441Srpaulo data->cmd_paddr = paddr; 1182286441Srpaulo data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) 1183286441Srpaulo + offsetof(struct iwm_tx_cmd, scratch); 1184286441Srpaulo paddr += sizeof(struct iwm_device_cmd); 1185286441Srpaulo 1186286441Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 1187286441Srpaulo if (error != 0) { 1188286441Srpaulo device_printf(sc->sc_dev, 1189286441Srpaulo "could not create TX buf DMA map\n"); 1190286441Srpaulo goto fail; 1191286441Srpaulo } 1192286441Srpaulo } 1193286441Srpaulo KASSERT(paddr == ring->cmd_dma.paddr + size, 1194286441Srpaulo ("invalid physical address")); 1195286441Srpaulo return 0; 1196286441Srpaulo 1197286441Srpaulofail: iwm_free_tx_ring(sc, ring); 1198286441Srpaulo return error; 1199286441Srpaulo} 1200286441Srpaulo 1201286441Srpaulostatic void 1202286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1203286441Srpaulo{ 1204286441Srpaulo int i; 1205286441Srpaulo 1206286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1207286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1208286441Srpaulo 1209286441Srpaulo if (data->m != NULL) { 1210286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1211286441Srpaulo BUS_DMASYNC_POSTWRITE); 1212286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1213286441Srpaulo m_freem(data->m); 1214286441Srpaulo data->m = NULL; 1215286441Srpaulo } 1216286441Srpaulo } 1217286441Srpaulo /* Clear TX descriptors. */ 1218286441Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1219286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1220286441Srpaulo BUS_DMASYNC_PREWRITE); 1221286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1222286441Srpaulo ring->queued = 0; 1223286441Srpaulo ring->cur = 0; 1224286441Srpaulo} 1225286441Srpaulo 1226286441Srpaulostatic void 1227286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1228286441Srpaulo{ 1229286441Srpaulo int i; 1230286441Srpaulo 1231286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1232286441Srpaulo iwm_dma_contig_free(&ring->cmd_dma); 1233286441Srpaulo 1234286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1235286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1236286441Srpaulo 1237286441Srpaulo if (data->m != NULL) { 1238286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1239286441Srpaulo BUS_DMASYNC_POSTWRITE); 1240286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1241286441Srpaulo m_freem(data->m); 1242286441Srpaulo data->m = NULL; 1243286441Srpaulo } 1244286441Srpaulo if (data->map != NULL) { 1245286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1246286441Srpaulo data->map = NULL; 1247286441Srpaulo } 1248286441Srpaulo } 1249286441Srpaulo if (ring->data_dmat != NULL) { 1250286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1251286441Srpaulo ring->data_dmat = NULL; 1252286441Srpaulo } 1253286441Srpaulo} 1254286441Srpaulo 1255286441Srpaulo/* 1256286441Srpaulo * High-level hardware frobbing routines 1257286441Srpaulo */ 1258286441Srpaulo 1259286441Srpaulostatic void 1260286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc) 1261286441Srpaulo{ 1262286441Srpaulo sc->sc_intmask = IWM_CSR_INI_SET_MASK; 1263286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1264286441Srpaulo} 1265286441Srpaulo 1266286441Srpaulostatic void 1267286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc) 1268286441Srpaulo{ 1269286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1270286441Srpaulo} 1271286441Srpaulo 1272286441Srpaulostatic void 1273286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc) 1274286441Srpaulo{ 1275286441Srpaulo /* disable interrupts */ 1276286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 1277286441Srpaulo 1278286441Srpaulo /* acknowledge all interrupts */ 1279286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1280286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0); 1281286441Srpaulo} 1282286441Srpaulo 1283286441Srpaulostatic void 1284286441Srpauloiwm_ict_reset(struct iwm_softc *sc) 1285286441Srpaulo{ 1286286441Srpaulo iwm_disable_interrupts(sc); 1287286441Srpaulo 1288286441Srpaulo /* Reset ICT table. */ 1289286441Srpaulo memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE); 1290286441Srpaulo sc->ict_cur = 0; 1291286441Srpaulo 1292286441Srpaulo /* Set physical address of ICT table (4KB aligned). */ 1293286441Srpaulo IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, 1294286441Srpaulo IWM_CSR_DRAM_INT_TBL_ENABLE 1295303628Ssbruno | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER 1296286441Srpaulo | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK 1297286441Srpaulo | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); 1298286441Srpaulo 1299286441Srpaulo /* Switch to ICT interrupt mode in driver. */ 1300286441Srpaulo sc->sc_flags |= IWM_FLAG_USE_ICT; 1301286441Srpaulo 1302286441Srpaulo /* Re-enable interrupts. */ 1303286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1304286441Srpaulo iwm_enable_interrupts(sc); 1305286441Srpaulo} 1306286441Srpaulo 1307286441Srpaulo/* iwlwifi pcie/trans.c */ 1308286441Srpaulo 1309286441Srpaulo/* 1310286441Srpaulo * Since this .. hard-resets things, it's time to actually 1311286441Srpaulo * mark the first vap (if any) as having no mac context. 1312286441Srpaulo * It's annoying, but since the driver is potentially being 1313286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we 1314286441Srpaulo * have to correctly track this. 1315286441Srpaulo */ 1316286441Srpaulostatic void 1317286441Srpauloiwm_stop_device(struct iwm_softc *sc) 1318286441Srpaulo{ 1319287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1320286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1321303628Ssbruno int chnl, qid; 1322303628Ssbruno uint32_t mask = 0; 1323286441Srpaulo 1324286441Srpaulo /* tell the device to stop sending interrupts */ 1325286441Srpaulo iwm_disable_interrupts(sc); 1326286441Srpaulo 1327286441Srpaulo /* 1328286441Srpaulo * FreeBSD-local: mark the first vap as not-uploaded, 1329286441Srpaulo * so the next transition through auth/assoc 1330286441Srpaulo * will correctly populate the MAC context. 1331286441Srpaulo */ 1332286441Srpaulo if (vap) { 1333286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 1334286441Srpaulo iv->is_uploaded = 0; 1335286441Srpaulo } 1336286441Srpaulo 1337286441Srpaulo /* device going down, Stop using ICT table */ 1338286441Srpaulo sc->sc_flags &= ~IWM_FLAG_USE_ICT; 1339286441Srpaulo 1340286441Srpaulo /* stop tx and rx. tx and rx bits, as usual, are from if_iwn */ 1341286441Srpaulo 1342286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1343286441Srpaulo 1344286441Srpaulo if (iwm_nic_lock(sc)) { 1345303628Ssbruno /* Stop each Tx DMA channel */ 1346286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1347286441Srpaulo IWM_WRITE(sc, 1348286441Srpaulo IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0); 1349303628Ssbruno mask |= IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE(chnl); 1350303628Ssbruno } 1351286441Srpaulo 1352303628Ssbruno /* Wait for DMA channels to be idle */ 1353303628Ssbruno if (!iwm_poll_bit(sc, IWM_FH_TSSR_TX_STATUS_REG, mask, mask, 1354303628Ssbruno 5000)) { 1355303628Ssbruno device_printf(sc->sc_dev, 1356303628Ssbruno "Failing on timeout while stopping DMA channel: [0x%08x]\n", 1357303628Ssbruno IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG)); 1358286441Srpaulo } 1359286441Srpaulo iwm_nic_unlock(sc); 1360286441Srpaulo } 1361301191Sadrian iwm_disable_rx_dma(sc); 1362286441Srpaulo 1363286441Srpaulo /* Stop RX ring. */ 1364286441Srpaulo iwm_reset_rx_ring(sc, &sc->rxq); 1365286441Srpaulo 1366286441Srpaulo /* Reset all TX rings. */ 1367286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) 1368286441Srpaulo iwm_reset_tx_ring(sc, &sc->txq[qid]); 1369286441Srpaulo 1370286441Srpaulo /* 1371286441Srpaulo * Power-down device's busmaster DMA clocks 1372286441Srpaulo */ 1373286441Srpaulo iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); 1374286441Srpaulo DELAY(5); 1375286441Srpaulo 1376286441Srpaulo /* Make sure (redundant) we've released our request to stay awake */ 1377286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 1378286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 1379286441Srpaulo 1380286441Srpaulo /* Stop the device, and put it in low power state */ 1381286441Srpaulo iwm_apm_stop(sc); 1382286441Srpaulo 1383286441Srpaulo /* Upon stop, the APM issues an interrupt if HW RF kill is set. 1384286441Srpaulo * Clean again the interrupt here 1385286441Srpaulo */ 1386286441Srpaulo iwm_disable_interrupts(sc); 1387286441Srpaulo /* stop and reset the on-board processor */ 1388303628Ssbruno IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); 1389286441Srpaulo 1390286441Srpaulo /* 1391286441Srpaulo * Even if we stop the HW, we still want the RF kill 1392286441Srpaulo * interrupt 1393286441Srpaulo */ 1394286441Srpaulo iwm_enable_rfkill_int(sc); 1395286441Srpaulo iwm_check_rfkill(sc); 1396286441Srpaulo} 1397286441Srpaulo 1398286441Srpaulo/* iwlwifi: mvm/ops.c */ 1399286441Srpaulostatic void 1400286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc) 1401286441Srpaulo{ 1402286441Srpaulo uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash; 1403286441Srpaulo uint32_t reg_val = 0; 1404286441Srpaulo 1405286441Srpaulo radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >> 1406286441Srpaulo IWM_FW_PHY_CFG_RADIO_TYPE_POS; 1407286441Srpaulo radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >> 1408286441Srpaulo IWM_FW_PHY_CFG_RADIO_STEP_POS; 1409286441Srpaulo radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >> 1410286441Srpaulo IWM_FW_PHY_CFG_RADIO_DASH_POS; 1411286441Srpaulo 1412286441Srpaulo /* SKU control */ 1413286441Srpaulo reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) << 1414286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; 1415286441Srpaulo reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) << 1416286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; 1417286441Srpaulo 1418286441Srpaulo /* radio configuration */ 1419286441Srpaulo reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; 1420286441Srpaulo reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; 1421286441Srpaulo reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; 1422286441Srpaulo 1423286441Srpaulo IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val); 1424286441Srpaulo 1425286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1426286441Srpaulo "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, 1427286441Srpaulo radio_cfg_step, radio_cfg_dash); 1428286441Srpaulo 1429286441Srpaulo /* 1430286441Srpaulo * W/A : NIC is stuck in a reset state after Early PCIe power off 1431286441Srpaulo * (PCIe power is lost before PERST# is asserted), causing ME FW 1432286441Srpaulo * to lose ownership and not being able to obtain it back. 1433286441Srpaulo */ 1434330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 1435303628Ssbruno iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, 1436303628Ssbruno IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, 1437303628Ssbruno ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); 1438303628Ssbruno } 1439286441Srpaulo} 1440286441Srpaulo 1441286441Srpaulostatic int 1442286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc) 1443286441Srpaulo{ 1444286441Srpaulo if (!iwm_nic_lock(sc)) 1445286441Srpaulo return EBUSY; 1446286441Srpaulo 1447286441Srpaulo /* 1448286441Srpaulo * Initialize RX ring. This is from the iwn driver. 1449286441Srpaulo */ 1450286441Srpaulo memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1451286441Srpaulo 1452286441Srpaulo /* stop DMA */ 1453301191Sadrian iwm_disable_rx_dma(sc); 1454286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); 1455286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); 1456286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0); 1457286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 1458286441Srpaulo 1459286441Srpaulo /* Set physical address of RX ring (256-byte aligned). */ 1460286441Srpaulo IWM_WRITE(sc, 1461286441Srpaulo IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8); 1462286441Srpaulo 1463286441Srpaulo /* Set physical address of RX status (16-byte aligned). */ 1464286441Srpaulo IWM_WRITE(sc, 1465286441Srpaulo IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); 1466286441Srpaulo 1467286441Srpaulo /* Enable RX. */ 1468286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 1469286441Srpaulo IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 1470286441Srpaulo IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ 1471286441Srpaulo IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 1472303628Ssbruno IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | 1473303628Ssbruno (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | 1474286441Srpaulo IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | 1475286441Srpaulo IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); 1476286441Srpaulo 1477286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF); 1478286441Srpaulo 1479286441Srpaulo /* W/A for interrupt coalescing bug in 7260 and 3160 */ 1480330166Seadler if (sc->cfg->host_interrupt_operation_mode) 1481286441Srpaulo IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE); 1482286441Srpaulo 1483286441Srpaulo /* 1484286441Srpaulo * Thus sayeth el jefe (iwlwifi) via a comment: 1485286441Srpaulo * 1486286441Srpaulo * This value should initially be 0 (before preparing any 1487303628Ssbruno * RBs), should be 8 after preparing the first 8 RBs (for example) 1488286441Srpaulo */ 1489286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); 1490286441Srpaulo 1491286441Srpaulo iwm_nic_unlock(sc); 1492286441Srpaulo 1493286441Srpaulo return 0; 1494286441Srpaulo} 1495286441Srpaulo 1496286441Srpaulostatic int 1497286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc) 1498286441Srpaulo{ 1499286441Srpaulo int qid; 1500286441Srpaulo 1501286441Srpaulo if (!iwm_nic_lock(sc)) 1502286441Srpaulo return EBUSY; 1503286441Srpaulo 1504286441Srpaulo /* Deactivate TX scheduler. */ 1505286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1506286441Srpaulo 1507286441Srpaulo /* Set physical address of "keep warm" page (16-byte aligned). */ 1508286441Srpaulo IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4); 1509286441Srpaulo 1510286441Srpaulo /* Initialize TX rings. */ 1511286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) { 1512286441Srpaulo struct iwm_tx_ring *txq = &sc->txq[qid]; 1513286441Srpaulo 1514286441Srpaulo /* Set physical address of TX ring (256-byte aligned). */ 1515286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid), 1516286441Srpaulo txq->desc_dma.paddr >> 8); 1517286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 1518286441Srpaulo "%s: loading ring %d descriptors (%p) at %lx\n", 1519286441Srpaulo __func__, 1520286441Srpaulo qid, txq->desc, 1521286441Srpaulo (unsigned long) (txq->desc_dma.paddr >> 8)); 1522286441Srpaulo } 1523303628Ssbruno 1524303628Ssbruno iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); 1525303628Ssbruno 1526286441Srpaulo iwm_nic_unlock(sc); 1527286441Srpaulo 1528286441Srpaulo return 0; 1529286441Srpaulo} 1530286441Srpaulo 1531286441Srpaulostatic int 1532286441Srpauloiwm_nic_init(struct iwm_softc *sc) 1533286441Srpaulo{ 1534286441Srpaulo int error; 1535286441Srpaulo 1536286441Srpaulo iwm_apm_init(sc); 1537330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 1538303628Ssbruno iwm_set_pwr(sc); 1539286441Srpaulo 1540286441Srpaulo iwm_mvm_nic_config(sc); 1541286441Srpaulo 1542286441Srpaulo if ((error = iwm_nic_rx_init(sc)) != 0) 1543286441Srpaulo return error; 1544286441Srpaulo 1545286441Srpaulo /* 1546286441Srpaulo * Ditto for TX, from iwn 1547286441Srpaulo */ 1548286441Srpaulo if ((error = iwm_nic_tx_init(sc)) != 0) 1549286441Srpaulo return error; 1550286441Srpaulo 1551286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1552286441Srpaulo "%s: shadow registers enabled\n", __func__); 1553286441Srpaulo IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff); 1554286441Srpaulo 1555286441Srpaulo return 0; 1556286441Srpaulo} 1557286441Srpaulo 1558286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = { 1559286441Srpaulo IWM_MVM_TX_FIFO_VO, 1560286441Srpaulo IWM_MVM_TX_FIFO_VI, 1561286441Srpaulo IWM_MVM_TX_FIFO_BE, 1562286441Srpaulo IWM_MVM_TX_FIFO_BK, 1563286441Srpaulo}; 1564286441Srpaulo 1565303628Ssbrunostatic int 1566303628Ssbrunoiwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) 1567286441Srpaulo{ 1568286441Srpaulo if (!iwm_nic_lock(sc)) { 1569286441Srpaulo device_printf(sc->sc_dev, 1570286441Srpaulo "%s: cannot enable txq %d\n", 1571286441Srpaulo __func__, 1572286441Srpaulo qid); 1573303628Ssbruno return EBUSY; 1574286441Srpaulo } 1575286441Srpaulo 1576303628Ssbruno IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); 1577286441Srpaulo 1578303628Ssbruno if (qid == IWM_MVM_CMD_QUEUE) { 1579303628Ssbruno /* unactivate before configuration */ 1580303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1581303628Ssbruno (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) 1582303628Ssbruno | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); 1583286441Srpaulo 1584303628Ssbruno iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); 1585286441Srpaulo 1586303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); 1587286441Srpaulo 1588303628Ssbruno iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); 1589303628Ssbruno /* Set scheduler window size and frame limit. */ 1590303628Ssbruno iwm_write_mem32(sc, 1591303628Ssbruno sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + 1592303628Ssbruno sizeof(uint32_t), 1593303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & 1594303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | 1595303628Ssbruno ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 1596303628Ssbruno IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); 1597286441Srpaulo 1598303628Ssbruno iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1599303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 1600303628Ssbruno (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | 1601303628Ssbruno (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | 1602303628Ssbruno IWM_SCD_QUEUE_STTS_REG_MSK); 1603303628Ssbruno } else { 1604303628Ssbruno struct iwm_scd_txq_cfg_cmd cmd; 1605303628Ssbruno int error; 1606286441Srpaulo 1607303628Ssbruno iwm_nic_unlock(sc); 1608303628Ssbruno 1609303628Ssbruno memset(&cmd, 0, sizeof(cmd)); 1610303628Ssbruno cmd.scd_queue = qid; 1611303628Ssbruno cmd.enable = 1; 1612303628Ssbruno cmd.sta_id = sta_id; 1613303628Ssbruno cmd.tx_fifo = fifo; 1614303628Ssbruno cmd.aggregate = 0; 1615303628Ssbruno cmd.window = IWM_FRAME_LIMIT; 1616303628Ssbruno 1617303628Ssbruno error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, 1618303628Ssbruno sizeof(cmd), &cmd); 1619303628Ssbruno if (error) { 1620303628Ssbruno device_printf(sc->sc_dev, 1621303628Ssbruno "cannot enable txq %d\n", qid); 1622303628Ssbruno return error; 1623303628Ssbruno } 1624303628Ssbruno 1625303628Ssbruno if (!iwm_nic_lock(sc)) 1626303628Ssbruno return EBUSY; 1627303628Ssbruno } 1628303628Ssbruno 1629303628Ssbruno iwm_write_prph(sc, IWM_SCD_EN_CTRL, 1630303628Ssbruno iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); 1631303628Ssbruno 1632286441Srpaulo iwm_nic_unlock(sc); 1633286441Srpaulo 1634303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_XMIT, "%s: enabled txq %d FIFO %d\n", 1635286441Srpaulo __func__, qid, fifo); 1636303628Ssbruno 1637303628Ssbruno return 0; 1638286441Srpaulo} 1639286441Srpaulo 1640286441Srpaulostatic int 1641286441Srpauloiwm_post_alive(struct iwm_softc *sc) 1642286441Srpaulo{ 1643286441Srpaulo int nwords; 1644286441Srpaulo int error, chnl; 1645303628Ssbruno uint32_t base; 1646286441Srpaulo 1647286441Srpaulo if (!iwm_nic_lock(sc)) 1648286441Srpaulo return EBUSY; 1649286441Srpaulo 1650303628Ssbruno base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); 1651303628Ssbruno if (sc->sched_base != base) { 1652286441Srpaulo device_printf(sc->sc_dev, 1653303628Ssbruno "%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", 1654303628Ssbruno __func__, sc->sched_base, base); 1655286441Srpaulo } 1656286441Srpaulo 1657286441Srpaulo iwm_ict_reset(sc); 1658286441Srpaulo 1659286441Srpaulo /* Clear TX scheduler state in SRAM. */ 1660286441Srpaulo nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - 1661286441Srpaulo IWM_SCD_CONTEXT_MEM_LOWER_BOUND) 1662286441Srpaulo / sizeof(uint32_t); 1663286441Srpaulo error = iwm_write_mem(sc, 1664286441Srpaulo sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, 1665286441Srpaulo NULL, nwords); 1666286441Srpaulo if (error) 1667286441Srpaulo goto out; 1668286441Srpaulo 1669286441Srpaulo /* Set physical address of TX scheduler rings (1KB aligned). */ 1670286441Srpaulo iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); 1671286441Srpaulo 1672286441Srpaulo iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); 1673286441Srpaulo 1674303628Ssbruno iwm_nic_unlock(sc); 1675303628Ssbruno 1676286441Srpaulo /* enable command channel */ 1677303628Ssbruno error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); 1678303628Ssbruno if (error) 1679303628Ssbruno return error; 1680286441Srpaulo 1681303628Ssbruno if (!iwm_nic_lock(sc)) 1682303628Ssbruno return EBUSY; 1683303628Ssbruno 1684286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); 1685286441Srpaulo 1686286441Srpaulo /* Enable DMA channels. */ 1687286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1688286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 1689286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 1690286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); 1691286441Srpaulo } 1692286441Srpaulo 1693286441Srpaulo IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, 1694286441Srpaulo IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); 1695286441Srpaulo 1696286441Srpaulo /* Enable L1-Active */ 1697330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 1698303628Ssbruno iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, 1699303628Ssbruno IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 1700303628Ssbruno } 1701286441Srpaulo 1702286441Srpaulo out: 1703303628Ssbruno iwm_nic_unlock(sc); 1704286441Srpaulo return error; 1705286441Srpaulo} 1706286441Srpaulo 1707286441Srpaulo/* 1708286441Srpaulo * NVM read access and content parsing. We do not support 1709286441Srpaulo * external NVM or writing NVM. 1710286441Srpaulo * iwlwifi/mvm/nvm.c 1711286441Srpaulo */ 1712286441Srpaulo 1713286441Srpaulo/* Default NVM size to read */ 1714303628Ssbruno#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) 1715286441Srpaulo 1716286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1 1717286441Srpaulo#define IWM_NVM_READ_OPCODE 0 1718286441Srpaulo 1719303628Ssbruno/* load nvm chunk response */ 1720330160Seadlerenum { 1721330160Seadler IWM_READ_NVM_CHUNK_SUCCEED = 0, 1722330160Seadler IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS = 1 1723330160Seadler}; 1724303628Ssbruno 1725286441Srpaulostatic int 1726286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, 1727286441Srpaulo uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) 1728286441Srpaulo{ 1729286441Srpaulo struct iwm_nvm_access_cmd nvm_access_cmd = { 1730286441Srpaulo .offset = htole16(offset), 1731286441Srpaulo .length = htole16(length), 1732286441Srpaulo .type = htole16(section), 1733286441Srpaulo .op_code = IWM_NVM_READ_OPCODE, 1734286441Srpaulo }; 1735286441Srpaulo struct iwm_nvm_access_resp *nvm_resp; 1736286441Srpaulo struct iwm_rx_packet *pkt; 1737286441Srpaulo struct iwm_host_cmd cmd = { 1738286441Srpaulo .id = IWM_NVM_ACCESS_CMD, 1739330160Seadler .flags = IWM_CMD_WANT_SKB | IWM_CMD_SEND_IN_RFKILL, 1740286441Srpaulo .data = { &nvm_access_cmd, }, 1741286441Srpaulo }; 1742330160Seadler int ret, bytes_read, offset_read; 1743286441Srpaulo uint8_t *resp_data; 1744286441Srpaulo 1745286441Srpaulo cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); 1746286441Srpaulo 1747286441Srpaulo ret = iwm_send_cmd(sc, &cmd); 1748303628Ssbruno if (ret) { 1749303628Ssbruno device_printf(sc->sc_dev, 1750303628Ssbruno "Could not send NVM_ACCESS command (error=%d)\n", ret); 1751286441Srpaulo return ret; 1752303628Ssbruno } 1753286441Srpaulo 1754286441Srpaulo pkt = cmd.resp_pkt; 1755286441Srpaulo 1756286441Srpaulo /* Extract NVM response */ 1757286441Srpaulo nvm_resp = (void *)pkt->data; 1758286441Srpaulo ret = le16toh(nvm_resp->status); 1759286441Srpaulo bytes_read = le16toh(nvm_resp->length); 1760286441Srpaulo offset_read = le16toh(nvm_resp->offset); 1761286441Srpaulo resp_data = nvm_resp->data; 1762286441Srpaulo if (ret) { 1763330160Seadler if ((offset != 0) && 1764330160Seadler (ret == IWM_READ_NVM_CHUNK_NOT_VALID_ADDRESS)) { 1765330160Seadler /* 1766330160Seadler * meaning of NOT_VALID_ADDRESS: 1767330160Seadler * driver try to read chunk from address that is 1768330160Seadler * multiple of 2K and got an error since addr is empty. 1769330160Seadler * meaning of (offset != 0): driver already 1770330160Seadler * read valid data from another chunk so this case 1771330160Seadler * is not an error. 1772330160Seadler */ 1773330160Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1774330160Seadler "NVM access command failed on offset 0x%x since that section size is multiple 2K\n", 1775330160Seadler offset); 1776330160Seadler *len = 0; 1777330160Seadler ret = 0; 1778330160Seadler } else { 1779330160Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1780330160Seadler "NVM access command failed with status %d\n", ret); 1781330160Seadler ret = EIO; 1782330160Seadler } 1783286441Srpaulo goto exit; 1784286441Srpaulo } 1785286441Srpaulo 1786286441Srpaulo if (offset_read != offset) { 1787286441Srpaulo device_printf(sc->sc_dev, 1788303628Ssbruno "NVM ACCESS response with invalid offset %d\n", 1789303628Ssbruno offset_read); 1790286441Srpaulo ret = EINVAL; 1791286441Srpaulo goto exit; 1792286441Srpaulo } 1793286441Srpaulo 1794303628Ssbruno if (bytes_read > length) { 1795303628Ssbruno device_printf(sc->sc_dev, 1796303628Ssbruno "NVM ACCESS response with too much data " 1797330160Seadler "(%d bytes requested, %d bytes received)\n", 1798303628Ssbruno length, bytes_read); 1799303628Ssbruno ret = EINVAL; 1800303628Ssbruno goto exit; 1801303628Ssbruno } 1802303628Ssbruno 1803330165Seadler /* Write data to NVM */ 1804286441Srpaulo memcpy(data + offset, resp_data, bytes_read); 1805286441Srpaulo *len = bytes_read; 1806286441Srpaulo 1807286441Srpaulo exit: 1808286441Srpaulo iwm_free_resp(sc, &cmd); 1809286441Srpaulo return ret; 1810286441Srpaulo} 1811286441Srpaulo 1812286441Srpaulo/* 1813286441Srpaulo * Reads an NVM section completely. 1814303628Ssbruno * NICs prior to 7000 family don't have a real NVM, but just read 1815286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited 1816286441Srpaulo * by uCode, we need to manually check in this case that we don't 1817286441Srpaulo * overflow and try to read more than the EEPROM size. 1818286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and 1819286441Srpaulo * the uCode fills the response with as much data as we can, 1820286441Srpaulo * without overflowing, so no check is needed. 1821286441Srpaulo */ 1822286441Srpaulostatic int 1823286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc, 1824330165Seadler uint16_t section, uint8_t *data, uint16_t *len, uint32_t size_read) 1825286441Srpaulo{ 1826330165Seadler uint16_t seglen, length, offset = 0; 1827330165Seadler int ret; 1828286441Srpaulo 1829330165Seadler /* Set nvm section read length */ 1830330165Seadler length = IWM_NVM_DEFAULT_CHUNK_SIZE; 1831303628Ssbruno 1832330165Seadler seglen = length; 1833286441Srpaulo 1834330165Seadler /* Read the NVM until exhausted (reading less than requested) */ 1835330165Seadler while (seglen == length) { 1836330165Seadler /* Check no memory assumptions fail and cause an overflow */ 1837330165Seadler if ((size_read + offset + length) > 1838330166Seadler sc->cfg->eeprom_size) { 1839330165Seadler device_printf(sc->sc_dev, 1840330165Seadler "EEPROM size is too small for NVM\n"); 1841330165Seadler return ENOBUFS; 1842286441Srpaulo } 1843330165Seadler 1844330165Seadler ret = iwm_nvm_read_chunk(sc, section, offset, length, data, &seglen); 1845330165Seadler if (ret) { 1846330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1847330165Seadler "Cannot read NVM from section %d offset %d, length %d\n", 1848330165Seadler section, offset, length); 1849330165Seadler return ret; 1850330165Seadler } 1851330165Seadler offset += seglen; 1852286441Srpaulo } 1853286441Srpaulo 1854330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 1855330165Seadler "NVM section %d read completed\n", section); 1856330165Seadler *len = offset; 1857330165Seadler return 0; 1858286441Srpaulo} 1859286441Srpaulo 1860286441Srpaulo/* 1861286441Srpaulo * BEGIN IWM_NVM_PARSE 1862286441Srpaulo */ 1863286441Srpaulo 1864286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */ 1865286441Srpaulo 1866286441Srpaulo/* NVM offsets (in words) definitions */ 1867303628Ssbrunoenum iwm_nvm_offsets { 1868286441Srpaulo /* NVM HW-Section offset (in words) definitions */ 1869286441Srpaulo IWM_HW_ADDR = 0x15, 1870286441Srpaulo 1871286441Srpaulo/* NVM SW-Section offset (in words) definitions */ 1872286441Srpaulo IWM_NVM_SW_SECTION = 0x1C0, 1873286441Srpaulo IWM_NVM_VERSION = 0, 1874286441Srpaulo IWM_RADIO_CFG = 1, 1875286441Srpaulo IWM_SKU = 2, 1876286441Srpaulo IWM_N_HW_ADDRS = 3, 1877286441Srpaulo IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION, 1878286441Srpaulo 1879286441Srpaulo/* NVM calibration section offset (in words) definitions */ 1880286441Srpaulo IWM_NVM_CALIB_SECTION = 0x2B8, 1881286441Srpaulo IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION 1882286441Srpaulo}; 1883286441Srpaulo 1884303628Ssbrunoenum iwm_8000_nvm_offsets { 1885303628Ssbruno /* NVM HW-Section offset (in words) definitions */ 1886303628Ssbruno IWM_HW_ADDR0_WFPM_8000 = 0x12, 1887303628Ssbruno IWM_HW_ADDR1_WFPM_8000 = 0x16, 1888303628Ssbruno IWM_HW_ADDR0_PCIE_8000 = 0x8A, 1889303628Ssbruno IWM_HW_ADDR1_PCIE_8000 = 0x8E, 1890303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000 = 1, 1891303628Ssbruno 1892303628Ssbruno /* NVM SW-Section offset (in words) definitions */ 1893303628Ssbruno IWM_NVM_SW_SECTION_8000 = 0x1C0, 1894303628Ssbruno IWM_NVM_VERSION_8000 = 0, 1895303628Ssbruno IWM_RADIO_CFG_8000 = 0, 1896303628Ssbruno IWM_SKU_8000 = 2, 1897303628Ssbruno IWM_N_HW_ADDRS_8000 = 3, 1898303628Ssbruno 1899303628Ssbruno /* NVM REGULATORY -Section offset (in words) definitions */ 1900303628Ssbruno IWM_NVM_CHANNELS_8000 = 0, 1901303628Ssbruno IWM_NVM_LAR_OFFSET_8000_OLD = 0x4C7, 1902303628Ssbruno IWM_NVM_LAR_OFFSET_8000 = 0x507, 1903303628Ssbruno IWM_NVM_LAR_ENABLED_8000 = 0x7, 1904303628Ssbruno 1905303628Ssbruno /* NVM calibration section offset (in words) definitions */ 1906303628Ssbruno IWM_NVM_CALIB_SECTION_8000 = 0x2B8, 1907303628Ssbruno IWM_XTAL_CALIB_8000 = 0x316 - IWM_NVM_CALIB_SECTION_8000 1908303628Ssbruno}; 1909303628Ssbruno 1910286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */ 1911286441Srpauloenum nvm_sku_bits { 1912286441Srpaulo IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), 1913286441Srpaulo IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), 1914286441Srpaulo IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), 1915286441Srpaulo IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), 1916286441Srpaulo}; 1917286441Srpaulo 1918286441Srpaulo/* radio config bits (actual values from NVM definition) */ 1919286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ 1920286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ 1921286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ 1922286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ 1923286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ 1924286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ 1925286441Srpaulo 1926303628Ssbruno#define IWM_NVM_RF_CFG_FLAVOR_MSK_8000(x) (x & 0xF) 1927303628Ssbruno#define IWM_NVM_RF_CFG_DASH_MSK_8000(x) ((x >> 4) & 0xF) 1928303628Ssbruno#define IWM_NVM_RF_CFG_STEP_MSK_8000(x) ((x >> 8) & 0xF) 1929303628Ssbruno#define IWM_NVM_RF_CFG_TYPE_MSK_8000(x) ((x >> 12) & 0xFFF) 1930303628Ssbruno#define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) 1931303628Ssbruno#define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) 1932303628Ssbruno 1933286441Srpaulo#define DEFAULT_MAX_TX_POWER 16 1934286441Srpaulo 1935286441Srpaulo/** 1936286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM 1937286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo 1938286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel 1939286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed 1940286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required 1941330165Seadler * XXX cannot find this (DFS) flag in iwm-nvm-parse.c 1942286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate 1943286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?) 1944286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) 1945286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) 1946286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) 1947286441Srpaulo */ 1948286441Srpauloenum iwm_nvm_channel_flags { 1949286441Srpaulo IWM_NVM_CHANNEL_VALID = (1 << 0), 1950286441Srpaulo IWM_NVM_CHANNEL_IBSS = (1 << 1), 1951286441Srpaulo IWM_NVM_CHANNEL_ACTIVE = (1 << 3), 1952286441Srpaulo IWM_NVM_CHANNEL_RADAR = (1 << 4), 1953286441Srpaulo IWM_NVM_CHANNEL_DFS = (1 << 7), 1954286441Srpaulo IWM_NVM_CHANNEL_WIDE = (1 << 8), 1955286441Srpaulo IWM_NVM_CHANNEL_40MHZ = (1 << 9), 1956286441Srpaulo IWM_NVM_CHANNEL_80MHZ = (1 << 10), 1957286441Srpaulo IWM_NVM_CHANNEL_160MHZ = (1 << 11), 1958286441Srpaulo}; 1959286441Srpaulo 1960286441Srpaulo/* 1961298877Savos * Translate EEPROM flags to net80211. 1962286441Srpaulo */ 1963298877Savosstatic uint32_t 1964298877Savosiwm_eeprom_channel_flags(uint16_t ch_flags) 1965286441Srpaulo{ 1966298877Savos uint32_t nflags; 1967286441Srpaulo 1968298877Savos nflags = 0; 1969298877Savos if ((ch_flags & IWM_NVM_CHANNEL_ACTIVE) == 0) 1970298877Savos nflags |= IEEE80211_CHAN_PASSIVE; 1971298877Savos if ((ch_flags & IWM_NVM_CHANNEL_IBSS) == 0) 1972298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1973298877Savos if (ch_flags & IWM_NVM_CHANNEL_RADAR) { 1974298877Savos nflags |= IEEE80211_CHAN_DFS; 1975298877Savos /* Just in case. */ 1976298877Savos nflags |= IEEE80211_CHAN_NOADHOC; 1977286441Srpaulo } 1978286441Srpaulo 1979298877Savos return (nflags); 1980286441Srpaulo} 1981286441Srpaulo 1982286441Srpaulostatic void 1983298877Savosiwm_add_channel_band(struct iwm_softc *sc, struct ieee80211_channel chans[], 1984303628Ssbruno int maxchans, int *nchans, int ch_idx, size_t ch_num, 1985303628Ssbruno const uint8_t bands[]) 1986286441Srpaulo{ 1987330165Seadler const uint16_t * const nvm_ch_flags = sc->nvm_data->nvm_ch_flags; 1988298877Savos uint32_t nflags; 1989286441Srpaulo uint16_t ch_flags; 1990298877Savos uint8_t ieee; 1991298877Savos int error; 1992286441Srpaulo 1993298877Savos for (; ch_idx < ch_num; ch_idx++) { 1994286441Srpaulo ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); 1995330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 1996303628Ssbruno ieee = iwm_nvm_channels[ch_idx]; 1997303628Ssbruno else 1998303628Ssbruno ieee = iwm_nvm_channels_8000[ch_idx]; 1999286441Srpaulo 2000286441Srpaulo if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { 2001286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 2002286441Srpaulo "Ch. %d Flags %x [%sGHz] - No traffic\n", 2003298877Savos ieee, ch_flags, 2004286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 2005286441Srpaulo "5.2" : "2.4"); 2006286441Srpaulo continue; 2007286441Srpaulo } 2008286441Srpaulo 2009298877Savos nflags = iwm_eeprom_channel_flags(ch_flags); 2010298877Savos error = ieee80211_add_channel(chans, maxchans, nchans, 2011298877Savos ieee, 0, 0, nflags, bands); 2012298877Savos if (error != 0) 2013298877Savos break; 2014286441Srpaulo 2015286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 2016286441Srpaulo "Ch. %d Flags %x [%sGHz] - Added\n", 2017298877Savos ieee, ch_flags, 2018286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 2019286441Srpaulo "5.2" : "2.4"); 2020286441Srpaulo } 2021286441Srpaulo} 2022286441Srpaulo 2023298877Savosstatic void 2024298877Savosiwm_init_channel_map(struct ieee80211com *ic, int maxchans, int *nchans, 2025298877Savos struct ieee80211_channel chans[]) 2026298877Savos{ 2027298877Savos struct iwm_softc *sc = ic->ic_softc; 2028330165Seadler struct iwm_nvm_data *data = sc->nvm_data; 2029299883Skevlo uint8_t bands[IEEE80211_MODE_BYTES]; 2030303628Ssbruno size_t ch_num; 2031298877Savos 2032298877Savos memset(bands, 0, sizeof(bands)); 2033298877Savos /* 1-13: 11b/g channels. */ 2034298877Savos setbit(bands, IEEE80211_MODE_11B); 2035298877Savos setbit(bands, IEEE80211_MODE_11G); 2036298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 0, 2037298877Savos IWM_NUM_2GHZ_CHANNELS - 1, bands); 2038298877Savos 2039298877Savos /* 14: 11b channel only. */ 2040298877Savos clrbit(bands, IEEE80211_MODE_11G); 2041298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 2042298936Savos IWM_NUM_2GHZ_CHANNELS - 1, IWM_NUM_2GHZ_CHANNELS, bands); 2043298877Savos 2044298877Savos if (data->sku_cap_band_52GHz_enable) { 2045330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 2046303628Ssbruno ch_num = nitems(iwm_nvm_channels); 2047303628Ssbruno else 2048303628Ssbruno ch_num = nitems(iwm_nvm_channels_8000); 2049298877Savos memset(bands, 0, sizeof(bands)); 2050298877Savos setbit(bands, IEEE80211_MODE_11A); 2051298877Savos iwm_add_channel_band(sc, chans, maxchans, nchans, 2052303628Ssbruno IWM_NUM_2GHZ_CHANNELS, ch_num, bands); 2053298877Savos } 2054298877Savos} 2055298877Savos 2056303628Ssbrunostatic void 2057330165Seadleriwm_set_hw_address_family_8000(struct iwm_softc *sc, struct iwm_nvm_data *data, 2058303628Ssbruno const uint16_t *mac_override, const uint16_t *nvm_hw) 2059303628Ssbruno{ 2060303628Ssbruno const uint8_t *hw_addr; 2061303628Ssbruno 2062303628Ssbruno if (mac_override) { 2063303628Ssbruno static const uint8_t reserved_mac[] = { 2064303628Ssbruno 0x02, 0xcc, 0xaa, 0xff, 0xee, 0x00 2065303628Ssbruno }; 2066303628Ssbruno 2067303628Ssbruno hw_addr = (const uint8_t *)(mac_override + 2068303628Ssbruno IWM_MAC_ADDRESS_OVERRIDE_8000); 2069303628Ssbruno 2070303628Ssbruno /* 2071303628Ssbruno * Store the MAC address from MAO section. 2072303628Ssbruno * No byte swapping is required in MAO section 2073303628Ssbruno */ 2074303628Ssbruno IEEE80211_ADDR_COPY(data->hw_addr, hw_addr); 2075303628Ssbruno 2076303628Ssbruno /* 2077303628Ssbruno * Force the use of the OTP MAC address in case of reserved MAC 2078303628Ssbruno * address in the NVM, or if address is given but invalid. 2079303628Ssbruno */ 2080303628Ssbruno if (!IEEE80211_ADDR_EQ(reserved_mac, hw_addr) && 2081303628Ssbruno !IEEE80211_ADDR_EQ(ieee80211broadcastaddr, data->hw_addr) && 2082303628Ssbruno iwm_is_valid_ether_addr(data->hw_addr) && 2083303628Ssbruno !IEEE80211_IS_MULTICAST(data->hw_addr)) 2084303628Ssbruno return; 2085303628Ssbruno 2086303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2087303628Ssbruno "%s: mac address from nvm override section invalid\n", 2088303628Ssbruno __func__); 2089303628Ssbruno } 2090303628Ssbruno 2091303628Ssbruno if (nvm_hw) { 2092303628Ssbruno /* read the mac address from WFMP registers */ 2093303628Ssbruno uint32_t mac_addr0 = 2094303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_0)); 2095303628Ssbruno uint32_t mac_addr1 = 2096303628Ssbruno htole32(iwm_read_prph(sc, IWM_WFMP_MAC_ADDR_1)); 2097303628Ssbruno 2098303628Ssbruno hw_addr = (const uint8_t *)&mac_addr0; 2099303628Ssbruno data->hw_addr[0] = hw_addr[3]; 2100303628Ssbruno data->hw_addr[1] = hw_addr[2]; 2101303628Ssbruno data->hw_addr[2] = hw_addr[1]; 2102303628Ssbruno data->hw_addr[3] = hw_addr[0]; 2103303628Ssbruno 2104303628Ssbruno hw_addr = (const uint8_t *)&mac_addr1; 2105303628Ssbruno data->hw_addr[4] = hw_addr[1]; 2106303628Ssbruno data->hw_addr[5] = hw_addr[0]; 2107303628Ssbruno 2108303628Ssbruno return; 2109303628Ssbruno } 2110303628Ssbruno 2111303628Ssbruno device_printf(sc->sc_dev, "%s: mac address not found\n", __func__); 2112303628Ssbruno memset(data->hw_addr, 0, sizeof(data->hw_addr)); 2113303628Ssbruno} 2114303628Ssbruno 2115286441Srpaulostatic int 2116303628Ssbrunoiwm_get_sku(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2117303628Ssbruno const uint16_t *phy_sku) 2118303628Ssbruno{ 2119330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2120303628Ssbruno return le16_to_cpup(nvm_sw + IWM_SKU); 2121303628Ssbruno 2122303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_SKU_8000)); 2123303628Ssbruno} 2124303628Ssbruno 2125303628Ssbrunostatic int 2126303628Ssbrunoiwm_get_nvm_version(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2127303628Ssbruno{ 2128330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2129303628Ssbruno return le16_to_cpup(nvm_sw + IWM_NVM_VERSION); 2130303628Ssbruno else 2131303628Ssbruno return le32_to_cpup((const uint32_t *)(nvm_sw + 2132303628Ssbruno IWM_NVM_VERSION_8000)); 2133303628Ssbruno} 2134303628Ssbruno 2135303628Ssbrunostatic int 2136303628Ssbrunoiwm_get_radio_cfg(const struct iwm_softc *sc, const uint16_t *nvm_sw, 2137303628Ssbruno const uint16_t *phy_sku) 2138303628Ssbruno{ 2139330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2140303628Ssbruno return le16_to_cpup(nvm_sw + IWM_RADIO_CFG); 2141303628Ssbruno 2142303628Ssbruno return le32_to_cpup((const uint32_t *)(phy_sku + IWM_RADIO_CFG_8000)); 2143303628Ssbruno} 2144303628Ssbruno 2145303628Ssbrunostatic int 2146303628Ssbrunoiwm_get_n_hw_addrs(const struct iwm_softc *sc, const uint16_t *nvm_sw) 2147303628Ssbruno{ 2148303628Ssbruno int n_hw_addr; 2149303628Ssbruno 2150330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) 2151303628Ssbruno return le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); 2152303628Ssbruno 2153303628Ssbruno n_hw_addr = le32_to_cpup((const uint32_t *)(nvm_sw + IWM_N_HW_ADDRS_8000)); 2154303628Ssbruno 2155303628Ssbruno return n_hw_addr & IWM_N_HW_ADDR_MASK; 2156303628Ssbruno} 2157303628Ssbruno 2158303628Ssbrunostatic void 2159303628Ssbrunoiwm_set_radio_cfg(const struct iwm_softc *sc, struct iwm_nvm_data *data, 2160303628Ssbruno uint32_t radio_cfg) 2161303628Ssbruno{ 2162330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2163303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); 2164303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); 2165303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); 2166303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); 2167303628Ssbruno return; 2168303628Ssbruno } 2169303628Ssbruno 2170303628Ssbruno /* set the radio configuration for family 8000 */ 2171303628Ssbruno data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK_8000(radio_cfg); 2172303628Ssbruno data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK_8000(radio_cfg); 2173303628Ssbruno data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK_8000(radio_cfg); 2174303628Ssbruno data->radio_cfg_pnum = IWM_NVM_RF_CFG_FLAVOR_MSK_8000(radio_cfg); 2175303628Ssbruno data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK_8000(radio_cfg); 2176303628Ssbruno data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK_8000(radio_cfg); 2177303628Ssbruno} 2178303628Ssbruno 2179303628Ssbrunostatic int 2180330165Seadleriwm_set_hw_address(struct iwm_softc *sc, struct iwm_nvm_data *data, 2181330165Seadler const uint16_t *nvm_hw, const uint16_t *mac_override) 2182330165Seadler{ 2183330165Seadler#ifdef notyet /* for FAMILY 9000 */ 2184330165Seadler if (cfg->mac_addr_from_csr) { 2185330165Seadler iwm_set_hw_address_from_csr(sc, data); 2186330165Seadler } else 2187330165Seadler#endif 2188330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2189330165Seadler const uint8_t *hw_addr = (const uint8_t *)(nvm_hw + IWM_HW_ADDR); 2190330165Seadler 2191330165Seadler /* The byte order is little endian 16 bit, meaning 214365 */ 2192330165Seadler data->hw_addr[0] = hw_addr[1]; 2193330165Seadler data->hw_addr[1] = hw_addr[0]; 2194330165Seadler data->hw_addr[2] = hw_addr[3]; 2195330165Seadler data->hw_addr[3] = hw_addr[2]; 2196330165Seadler data->hw_addr[4] = hw_addr[5]; 2197330165Seadler data->hw_addr[5] = hw_addr[4]; 2198330165Seadler } else { 2199330165Seadler iwm_set_hw_address_family_8000(sc, data, mac_override, nvm_hw); 2200330165Seadler } 2201330165Seadler 2202330165Seadler if (!iwm_is_valid_ether_addr(data->hw_addr)) { 2203330165Seadler device_printf(sc->sc_dev, "no valid mac address was found\n"); 2204330165Seadler return EINVAL; 2205330165Seadler } 2206330165Seadler 2207330165Seadler return 0; 2208330165Seadler} 2209330165Seadler 2210330165Seadlerstatic struct iwm_nvm_data * 2211286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc, 2212303628Ssbruno const uint16_t *nvm_hw, const uint16_t *nvm_sw, 2213303628Ssbruno const uint16_t *nvm_calib, const uint16_t *mac_override, 2214303628Ssbruno const uint16_t *phy_sku, const uint16_t *regulatory) 2215286441Srpaulo{ 2216330165Seadler struct iwm_nvm_data *data; 2217303628Ssbruno uint32_t sku, radio_cfg; 2218286441Srpaulo 2219330166Seadler if (sc->cfg->device_family != IWM_DEVICE_FAMILY_8000) { 2220330165Seadler data = malloc(sizeof(*data) + 2221330165Seadler IWM_NUM_CHANNELS * sizeof(uint16_t), 2222330165Seadler M_DEVBUF, M_NOWAIT | M_ZERO); 2223330165Seadler } else { 2224330165Seadler data = malloc(sizeof(*data) + 2225330165Seadler IWM_NUM_CHANNELS_8000 * sizeof(uint16_t), 2226330165Seadler M_DEVBUF, M_NOWAIT | M_ZERO); 2227330165Seadler } 2228330165Seadler if (!data) 2229330165Seadler return NULL; 2230330165Seadler 2231303628Ssbruno data->nvm_version = iwm_get_nvm_version(sc, nvm_sw); 2232286441Srpaulo 2233303628Ssbruno radio_cfg = iwm_get_radio_cfg(sc, nvm_sw, phy_sku); 2234303628Ssbruno iwm_set_radio_cfg(sc, data, radio_cfg); 2235286441Srpaulo 2236303628Ssbruno sku = iwm_get_sku(sc, nvm_sw, phy_sku); 2237286441Srpaulo data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; 2238286441Srpaulo data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; 2239286441Srpaulo data->sku_cap_11n_enable = 0; 2240286441Srpaulo 2241303628Ssbruno data->n_hw_addrs = iwm_get_n_hw_addrs(sc, nvm_sw); 2242286441Srpaulo 2243330165Seadler /* If no valid mac address was found - bail out */ 2244330165Seadler if (iwm_set_hw_address(sc, data, nvm_hw, mac_override)) { 2245330165Seadler free(data, M_DEVBUF); 2246330165Seadler return NULL; 2247303628Ssbruno } 2248286441Srpaulo 2249330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 2250303628Ssbruno memcpy(data->nvm_ch_flags, &nvm_sw[IWM_NVM_CHANNELS], 2251303628Ssbruno IWM_NUM_CHANNELS * sizeof(uint16_t)); 2252303628Ssbruno } else { 2253303628Ssbruno memcpy(data->nvm_ch_flags, ®ulatory[IWM_NVM_CHANNELS_8000], 2254303628Ssbruno IWM_NUM_CHANNELS_8000 * sizeof(uint16_t)); 2255303628Ssbruno } 2256286441Srpaulo 2257330165Seadler return data; 2258286441Srpaulo} 2259286441Srpaulo 2260330165Seadlerstatic void 2261330165Seadleriwm_free_nvm_data(struct iwm_nvm_data *data) 2262330165Seadler{ 2263330165Seadler if (data != NULL) 2264330165Seadler free(data, M_DEVBUF); 2265330165Seadler} 2266286441Srpaulo 2267330165Seadlerstatic struct iwm_nvm_data * 2268286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) 2269286441Srpaulo{ 2270303628Ssbruno const uint16_t *hw, *sw, *calib, *regulatory, *mac_override, *phy_sku; 2271286441Srpaulo 2272286441Srpaulo /* Checking for required sections */ 2273330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) { 2274303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2275330166Seadler !sections[sc->cfg->nvm_hw_section_num].data) { 2276303628Ssbruno device_printf(sc->sc_dev, 2277303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2278330165Seadler return NULL; 2279303628Ssbruno } 2280330166Seadler } else if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 2281303628Ssbruno /* SW and REGULATORY sections are mandatory */ 2282303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 2283303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_REGULATORY].data) { 2284303628Ssbruno device_printf(sc->sc_dev, 2285303628Ssbruno "Can't parse empty OTP/NVM sections\n"); 2286330165Seadler return NULL; 2287303628Ssbruno } 2288303628Ssbruno /* MAC_OVERRIDE or at least HW section must exist */ 2289330166Seadler if (!sections[sc->cfg->nvm_hw_section_num].data && 2290303628Ssbruno !sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data) { 2291303628Ssbruno device_printf(sc->sc_dev, 2292303628Ssbruno "Can't parse mac_address, empty sections\n"); 2293330165Seadler return NULL; 2294303628Ssbruno } 2295303628Ssbruno 2296303628Ssbruno /* PHY_SKU section is mandatory in B0 */ 2297303628Ssbruno if (!sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data) { 2298303628Ssbruno device_printf(sc->sc_dev, 2299303628Ssbruno "Can't parse phy_sku in B0, empty sections\n"); 2300330165Seadler return NULL; 2301303628Ssbruno } 2302303628Ssbruno } else { 2303330166Seadler panic("unknown device family %d\n", sc->cfg->device_family); 2304286441Srpaulo } 2305286441Srpaulo 2306330166Seadler hw = (const uint16_t *) sections[sc->cfg->nvm_hw_section_num].data; 2307286441Srpaulo sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; 2308303628Ssbruno calib = (const uint16_t *) 2309303628Ssbruno sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; 2310303628Ssbruno regulatory = (const uint16_t *) 2311303628Ssbruno sections[IWM_NVM_SECTION_TYPE_REGULATORY].data; 2312303628Ssbruno mac_override = (const uint16_t *) 2313303628Ssbruno sections[IWM_NVM_SECTION_TYPE_MAC_OVERRIDE].data; 2314303628Ssbruno phy_sku = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_PHY_SKU].data; 2315303628Ssbruno 2316303628Ssbruno return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override, 2317303628Ssbruno phy_sku, regulatory); 2318286441Srpaulo} 2319286441Srpaulo 2320286441Srpaulostatic int 2321286441Srpauloiwm_nvm_init(struct iwm_softc *sc) 2322286441Srpaulo{ 2323330165Seadler struct iwm_nvm_section nvm_sections[IWM_NVM_MAX_NUM_SECTIONS]; 2324330165Seadler int i, ret, section; 2325330165Seadler uint32_t size_read = 0; 2326330165Seadler uint8_t *nvm_buffer, *temp; 2327286441Srpaulo uint16_t len; 2328286441Srpaulo 2329330165Seadler memset(nvm_sections, 0, sizeof(nvm_sections)); 2330286441Srpaulo 2331330166Seadler if (sc->cfg->nvm_hw_section_num >= IWM_NVM_MAX_NUM_SECTIONS) 2332330165Seadler return EINVAL; 2333301970Sadrian 2334330165Seadler /* load NVM values from nic */ 2335330165Seadler /* Read From FW NVM */ 2336330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, "Read from NVM\n"); 2337286441Srpaulo 2338330166Seadler nvm_buffer = malloc(sc->cfg->eeprom_size, M_DEVBUF, M_NOWAIT | M_ZERO); 2339330165Seadler if (!nvm_buffer) 2340330165Seadler return ENOMEM; 2341330165Seadler for (section = 0; section < IWM_NVM_MAX_NUM_SECTIONS; section++) { 2342330165Seadler /* we override the constness for initial read */ 2343330165Seadler ret = iwm_nvm_read_section(sc, section, nvm_buffer, 2344330165Seadler &len, size_read); 2345330165Seadler if (ret) 2346303628Ssbruno continue; 2347330165Seadler size_read += len; 2348330165Seadler temp = malloc(len, M_DEVBUF, M_NOWAIT); 2349330165Seadler if (!temp) { 2350330165Seadler ret = ENOMEM; 2351286441Srpaulo break; 2352286441Srpaulo } 2353330165Seadler memcpy(temp, nvm_buffer, len); 2354330165Seadler 2355330165Seadler nvm_sections[section].data = temp; 2356286441Srpaulo nvm_sections[section].length = len; 2357286441Srpaulo } 2358330165Seadler if (!size_read) 2359330165Seadler device_printf(sc->sc_dev, "OTP is blank\n"); 2360330165Seadler free(nvm_buffer, M_DEVBUF); 2361286441Srpaulo 2362330165Seadler sc->nvm_data = iwm_parse_nvm_sections(sc, nvm_sections); 2363330165Seadler if (!sc->nvm_data) 2364330165Seadler return EINVAL; 2365330165Seadler IWM_DPRINTF(sc, IWM_DEBUG_EEPROM | IWM_DEBUG_RESET, 2366330165Seadler "nvm version = %x\n", sc->nvm_data->nvm_version); 2367330165Seadler 2368330165Seadler for (i = 0; i < IWM_NVM_MAX_NUM_SECTIONS; i++) { 2369301970Sadrian if (nvm_sections[i].data != NULL) 2370301970Sadrian free(nvm_sections[i].data, M_DEVBUF); 2371301970Sadrian } 2372301970Sadrian 2373330165Seadler return 0; 2374286441Srpaulo} 2375286441Srpaulo 2376286441Srpaulo/* 2377286441Srpaulo * Firmware loading gunk. This is kind of a weird hybrid between the 2378286441Srpaulo * iwn driver and the Linux iwlwifi driver. 2379286441Srpaulo */ 2380286441Srpaulo 2381286441Srpaulostatic int 2382303628Ssbrunoiwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr, 2383286441Srpaulo const uint8_t *section, uint32_t byte_cnt) 2384286441Srpaulo{ 2385303628Ssbruno int error = EINVAL; 2386303628Ssbruno uint32_t chunk_sz, offset; 2387303628Ssbruno 2388303628Ssbruno chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt); 2389303628Ssbruno 2390303628Ssbruno for (offset = 0; offset < byte_cnt; offset += chunk_sz) { 2391303628Ssbruno uint32_t addr, len; 2392303628Ssbruno const uint8_t *data; 2393303628Ssbruno 2394303628Ssbruno addr = dst_addr + offset; 2395303628Ssbruno len = MIN(chunk_sz, byte_cnt - offset); 2396303628Ssbruno data = section + offset; 2397303628Ssbruno 2398303628Ssbruno error = iwm_firmware_load_chunk(sc, addr, data, len); 2399303628Ssbruno if (error) 2400303628Ssbruno break; 2401303628Ssbruno } 2402303628Ssbruno 2403303628Ssbruno return error; 2404303628Ssbruno} 2405303628Ssbruno 2406303628Ssbrunostatic int 2407303628Ssbrunoiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, 2408303628Ssbruno const uint8_t *chunk, uint32_t byte_cnt) 2409303628Ssbruno{ 2410286441Srpaulo struct iwm_dma_info *dma = &sc->fw_dma; 2411286441Srpaulo int error; 2412286441Srpaulo 2413303628Ssbruno /* Copy firmware chunk into pre-allocated DMA-safe memory. */ 2414303628Ssbruno memcpy(dma->vaddr, chunk, byte_cnt); 2415286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 2416286441Srpaulo 2417303628Ssbruno if (dst_addr >= IWM_FW_MEM_EXTENDED_START && 2418303628Ssbruno dst_addr <= IWM_FW_MEM_EXTENDED_END) { 2419303628Ssbruno iwm_set_bits_prph(sc, IWM_LMPM_CHICK, 2420303628Ssbruno IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2421303628Ssbruno } 2422303628Ssbruno 2423303628Ssbruno sc->sc_fw_chunk_done = 0; 2424303628Ssbruno 2425286441Srpaulo if (!iwm_nic_lock(sc)) 2426286441Srpaulo return EBUSY; 2427286441Srpaulo 2428286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2429286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 2430286441Srpaulo IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), 2431286441Srpaulo dst_addr); 2432286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), 2433286441Srpaulo dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 2434286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), 2435286441Srpaulo (iwm_get_dma_hi_addr(dma->paddr) 2436286441Srpaulo << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 2437286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 2438286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 2439286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | 2440286441Srpaulo IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 2441286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 2442286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 2443286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 2444286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 2445286441Srpaulo 2446286441Srpaulo iwm_nic_unlock(sc); 2447286441Srpaulo 2448286441Srpaulo /* wait 1s for this segment to load */ 2449286441Srpaulo while (!sc->sc_fw_chunk_done) 2450286441Srpaulo if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0) 2451286441Srpaulo break; 2452286441Srpaulo 2453303628Ssbruno if (!sc->sc_fw_chunk_done) { 2454303628Ssbruno device_printf(sc->sc_dev, 2455303628Ssbruno "fw chunk addr 0x%x len %d failed to load\n", 2456303628Ssbruno dst_addr, byte_cnt); 2457303628Ssbruno } 2458303628Ssbruno 2459303628Ssbruno if (dst_addr >= IWM_FW_MEM_EXTENDED_START && 2460303628Ssbruno dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) { 2461303628Ssbruno iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, 2462303628Ssbruno IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); 2463303628Ssbruno iwm_nic_unlock(sc); 2464303628Ssbruno } 2465303628Ssbruno 2466286441Srpaulo return error; 2467286441Srpaulo} 2468286441Srpaulo 2469303628Ssbrunoint 2470303628Ssbrunoiwm_load_cpu_sections_8000(struct iwm_softc *sc, struct iwm_fw_sects *fws, 2471303628Ssbruno int cpu, int *first_ucode_section) 2472303628Ssbruno{ 2473303628Ssbruno int shift_param; 2474303628Ssbruno int i, error = 0, sec_num = 0x1; 2475303628Ssbruno uint32_t val, last_read_idx = 0; 2476303628Ssbruno const void *data; 2477303628Ssbruno uint32_t dlen; 2478303628Ssbruno uint32_t offset; 2479303628Ssbruno 2480303628Ssbruno if (cpu == 1) { 2481303628Ssbruno shift_param = 0; 2482303628Ssbruno *first_ucode_section = 0; 2483303628Ssbruno } else { 2484303628Ssbruno shift_param = 16; 2485303628Ssbruno (*first_ucode_section)++; 2486303628Ssbruno } 2487303628Ssbruno 2488303628Ssbruno for (i = *first_ucode_section; i < IWM_UCODE_SECT_MAX; i++) { 2489303628Ssbruno last_read_idx = i; 2490303628Ssbruno data = fws->fw_sect[i].fws_data; 2491303628Ssbruno dlen = fws->fw_sect[i].fws_len; 2492303628Ssbruno offset = fws->fw_sect[i].fws_devoff; 2493303628Ssbruno 2494303628Ssbruno /* 2495303628Ssbruno * CPU1_CPU2_SEPARATOR_SECTION delimiter - separate between 2496303628Ssbruno * CPU1 to CPU2. 2497303628Ssbruno * PAGING_SEPARATOR_SECTION delimiter - separate between 2498303628Ssbruno * CPU2 non paged to CPU2 paging sec. 2499303628Ssbruno */ 2500303628Ssbruno if (!data || offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || 2501303628Ssbruno offset == IWM_PAGING_SEPARATOR_SECTION) 2502303628Ssbruno break; 2503303628Ssbruno 2504303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2505303628Ssbruno "LOAD FIRMWARE chunk %d offset 0x%x len %d for cpu %d\n", 2506303628Ssbruno i, offset, dlen, cpu); 2507303628Ssbruno 2508303628Ssbruno if (dlen > sc->sc_fwdmasegsz) { 2509303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2510303628Ssbruno "chunk %d too large (%d bytes)\n", i, dlen); 2511303628Ssbruno error = EFBIG; 2512303628Ssbruno } else { 2513303628Ssbruno error = iwm_firmware_load_sect(sc, offset, data, dlen); 2514303628Ssbruno } 2515303628Ssbruno if (error) { 2516303628Ssbruno device_printf(sc->sc_dev, 2517303628Ssbruno "could not load firmware chunk %d (error %d)\n", 2518303628Ssbruno i, error); 2519303628Ssbruno return error; 2520303628Ssbruno } 2521303628Ssbruno 2522303628Ssbruno /* Notify the ucode of the loaded section number and status */ 2523303628Ssbruno if (iwm_nic_lock(sc)) { 2524303628Ssbruno val = IWM_READ(sc, IWM_FH_UCODE_LOAD_STATUS); 2525303628Ssbruno val = val | (sec_num << shift_param); 2526303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, val); 2527303628Ssbruno sec_num = (sec_num << 1) | 0x1; 2528303628Ssbruno iwm_nic_unlock(sc); 2529303628Ssbruno 2530303628Ssbruno /* 2531303628Ssbruno * The firmware won't load correctly without this delay. 2532303628Ssbruno */ 2533303628Ssbruno DELAY(8000); 2534303628Ssbruno } 2535303628Ssbruno } 2536303628Ssbruno 2537303628Ssbruno *first_ucode_section = last_read_idx; 2538303628Ssbruno 2539303628Ssbruno if (iwm_nic_lock(sc)) { 2540303628Ssbruno if (cpu == 1) 2541303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFF); 2542303628Ssbruno else 2543303628Ssbruno IWM_WRITE(sc, IWM_FH_UCODE_LOAD_STATUS, 0xFFFFFFFF); 2544303628Ssbruno iwm_nic_unlock(sc); 2545303628Ssbruno } 2546303628Ssbruno 2547303628Ssbruno return 0; 2548303628Ssbruno} 2549303628Ssbruno 2550303628Ssbrunoint 2551303628Ssbrunoiwm_load_firmware_8000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2552303628Ssbruno{ 2553303628Ssbruno struct iwm_fw_sects *fws; 2554303628Ssbruno int error = 0; 2555303628Ssbruno int first_ucode_section; 2556303628Ssbruno 2557303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, "loading ucode type %d\n", 2558303628Ssbruno ucode_type); 2559303628Ssbruno 2560303628Ssbruno fws = &sc->sc_fw.fw_sects[ucode_type]; 2561303628Ssbruno 2562303628Ssbruno /* configure the ucode to be ready to get the secured image */ 2563303628Ssbruno /* release CPU reset */ 2564303628Ssbruno iwm_write_prph(sc, IWM_RELEASE_CPU_RESET, IWM_RELEASE_CPU_RESET_BIT); 2565303628Ssbruno 2566303628Ssbruno /* load to FW the binary Secured sections of CPU1 */ 2567303628Ssbruno error = iwm_load_cpu_sections_8000(sc, fws, 1, &first_ucode_section); 2568303628Ssbruno if (error) 2569303628Ssbruno return error; 2570303628Ssbruno 2571303628Ssbruno /* load to FW the binary sections of CPU2 */ 2572303628Ssbruno return iwm_load_cpu_sections_8000(sc, fws, 2, &first_ucode_section); 2573303628Ssbruno} 2574303628Ssbruno 2575286441Srpaulostatic int 2576303628Ssbrunoiwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2577286441Srpaulo{ 2578286441Srpaulo struct iwm_fw_sects *fws; 2579303628Ssbruno int error, i; 2580286441Srpaulo const void *data; 2581286441Srpaulo uint32_t dlen; 2582286441Srpaulo uint32_t offset; 2583286441Srpaulo 2584286441Srpaulo sc->sc_uc.uc_intr = 0; 2585286441Srpaulo 2586286441Srpaulo fws = &sc->sc_fw.fw_sects[ucode_type]; 2587286441Srpaulo for (i = 0; i < fws->fw_count; i++) { 2588286441Srpaulo data = fws->fw_sect[i].fws_data; 2589286441Srpaulo dlen = fws->fw_sect[i].fws_len; 2590286441Srpaulo offset = fws->fw_sect[i].fws_devoff; 2591286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 2592286441Srpaulo "LOAD FIRMWARE type %d offset %u len %d\n", 2593286441Srpaulo ucode_type, offset, dlen); 2594303628Ssbruno if (dlen > sc->sc_fwdmasegsz) { 2595303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 2596303628Ssbruno "chunk %d too large (%d bytes)\n", i, dlen); 2597303628Ssbruno error = EFBIG; 2598303628Ssbruno } else { 2599303628Ssbruno error = iwm_firmware_load_sect(sc, offset, data, dlen); 2600303628Ssbruno } 2601286441Srpaulo if (error) { 2602286441Srpaulo device_printf(sc->sc_dev, 2603303628Ssbruno "could not load firmware chunk %u of %u " 2604303628Ssbruno "(error=%d)\n", i, fws->fw_count, error); 2605286441Srpaulo return error; 2606286441Srpaulo } 2607286441Srpaulo } 2608286441Srpaulo 2609286441Srpaulo IWM_WRITE(sc, IWM_CSR_RESET, 0); 2610286441Srpaulo 2611303628Ssbruno return 0; 2612303628Ssbruno} 2613303628Ssbruno 2614303628Ssbrunostatic int 2615303628Ssbrunoiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2616303628Ssbruno{ 2617303628Ssbruno int error, w; 2618303628Ssbruno 2619330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 2620303628Ssbruno error = iwm_load_firmware_8000(sc, ucode_type); 2621303628Ssbruno else 2622303628Ssbruno error = iwm_load_firmware_7000(sc, ucode_type); 2623303628Ssbruno if (error) 2624303628Ssbruno return error; 2625303628Ssbruno 2626303628Ssbruno /* wait for the firmware to load */ 2627286441Srpaulo for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { 2628286441Srpaulo error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10); 2629286441Srpaulo } 2630303628Ssbruno if (error || !sc->sc_uc.uc_ok) { 2631303628Ssbruno device_printf(sc->sc_dev, "could not load firmware\n"); 2632330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 2633303628Ssbruno device_printf(sc->sc_dev, "cpu1 status: 0x%x\n", 2634303628Ssbruno iwm_read_prph(sc, IWM_SB_CPU_1_STATUS)); 2635303628Ssbruno device_printf(sc->sc_dev, "cpu2 status: 0x%x\n", 2636303628Ssbruno iwm_read_prph(sc, IWM_SB_CPU_2_STATUS)); 2637303628Ssbruno } 2638303628Ssbruno } 2639286441Srpaulo 2640303628Ssbruno /* 2641303628Ssbruno * Give the firmware some time to initialize. 2642303628Ssbruno * Accessing it too early causes errors. 2643303628Ssbruno */ 2644303628Ssbruno msleep(&w, &sc->sc_mtx, 0, "iwmfwinit", hz); 2645303628Ssbruno 2646286441Srpaulo return error; 2647286441Srpaulo} 2648286441Srpaulo 2649286441Srpaulo/* iwlwifi: pcie/trans.c */ 2650286441Srpaulostatic int 2651286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 2652286441Srpaulo{ 2653286441Srpaulo int error; 2654286441Srpaulo 2655286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 2656286441Srpaulo 2657286441Srpaulo if ((error = iwm_nic_init(sc)) != 0) { 2658286441Srpaulo device_printf(sc->sc_dev, "unable to init nic\n"); 2659286441Srpaulo return error; 2660286441Srpaulo } 2661286441Srpaulo 2662286441Srpaulo /* make sure rfkill handshake bits are cleared */ 2663286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2664286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, 2665286441Srpaulo IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 2666286441Srpaulo 2667286441Srpaulo /* clear (again), then enable host interrupts */ 2668286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 2669286441Srpaulo iwm_enable_interrupts(sc); 2670286441Srpaulo 2671286441Srpaulo /* really make sure rfkill handshake bits are cleared */ 2672286441Srpaulo /* maybe we should write a few times more? just to make sure */ 2673286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2674286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 2675286441Srpaulo 2676286441Srpaulo /* Load the given image to the HW */ 2677286441Srpaulo return iwm_load_firmware(sc, ucode_type); 2678286441Srpaulo} 2679286441Srpaulo 2680286441Srpaulostatic int 2681286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) 2682286441Srpaulo{ 2683286441Srpaulo struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { 2684286441Srpaulo .valid = htole32(valid_tx_ant), 2685286441Srpaulo }; 2686286441Srpaulo 2687286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, 2688286441Srpaulo IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); 2689286441Srpaulo} 2690286441Srpaulo 2691286441Srpaulo/* iwlwifi: mvm/fw.c */ 2692286441Srpaulostatic int 2693286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc) 2694286441Srpaulo{ 2695286441Srpaulo struct iwm_phy_cfg_cmd phy_cfg_cmd; 2696286441Srpaulo enum iwm_ucode_type ucode_type = sc->sc_uc_current; 2697286441Srpaulo 2698286441Srpaulo /* Set parameters */ 2699286441Srpaulo phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); 2700286441Srpaulo phy_cfg_cmd.calib_control.event_trigger = 2701286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger; 2702286441Srpaulo phy_cfg_cmd.calib_control.flow_trigger = 2703286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger; 2704286441Srpaulo 2705286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 2706286441Srpaulo "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); 2707286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, 2708286441Srpaulo sizeof(phy_cfg_cmd), &phy_cfg_cmd); 2709286441Srpaulo} 2710286441Srpaulo 2711286441Srpaulostatic int 2712286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, 2713286441Srpaulo enum iwm_ucode_type ucode_type) 2714286441Srpaulo{ 2715286441Srpaulo enum iwm_ucode_type old_type = sc->sc_uc_current; 2716286441Srpaulo int error; 2717286441Srpaulo 2718303628Ssbruno if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { 2719303628Ssbruno device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", 2720303628Ssbruno error); 2721286441Srpaulo return error; 2722303628Ssbruno } 2723286441Srpaulo 2724286441Srpaulo sc->sc_uc_current = ucode_type; 2725286441Srpaulo error = iwm_start_fw(sc, ucode_type); 2726286441Srpaulo if (error) { 2727303628Ssbruno device_printf(sc->sc_dev, "iwm_start_fw: failed %d\n", error); 2728286441Srpaulo sc->sc_uc_current = old_type; 2729286441Srpaulo return error; 2730286441Srpaulo } 2731286441Srpaulo 2732303628Ssbruno error = iwm_post_alive(sc); 2733303628Ssbruno if (error) { 2734303628Ssbruno device_printf(sc->sc_dev, "iwm_fw_alive: failed %d\n", error); 2735303628Ssbruno } 2736303628Ssbruno return error; 2737286441Srpaulo} 2738286441Srpaulo 2739286441Srpaulo/* 2740286441Srpaulo * mvm misc bits 2741286441Srpaulo */ 2742286441Srpaulo 2743286441Srpaulo/* 2744286441Srpaulo * follows iwlwifi/fw.c 2745286441Srpaulo */ 2746286441Srpaulostatic int 2747286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) 2748286441Srpaulo{ 2749286441Srpaulo int error; 2750286441Srpaulo 2751286441Srpaulo /* do not operate with rfkill switch turned on */ 2752286441Srpaulo if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { 2753286441Srpaulo device_printf(sc->sc_dev, 2754286441Srpaulo "radio is disabled by hardware switch\n"); 2755286441Srpaulo return EPERM; 2756286441Srpaulo } 2757286441Srpaulo 2758286441Srpaulo sc->sc_init_complete = 0; 2759286441Srpaulo if ((error = iwm_mvm_load_ucode_wait_alive(sc, 2760301192Sadrian IWM_UCODE_TYPE_INIT)) != 0) { 2761301192Sadrian device_printf(sc->sc_dev, "failed to load init firmware\n"); 2762286441Srpaulo return error; 2763301192Sadrian } 2764286441Srpaulo 2765286441Srpaulo if (justnvm) { 2766286441Srpaulo if ((error = iwm_nvm_init(sc)) != 0) { 2767286441Srpaulo device_printf(sc->sc_dev, "failed to read nvm\n"); 2768286441Srpaulo return error; 2769286441Srpaulo } 2770330165Seadler IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, sc->nvm_data->hw_addr); 2771286441Srpaulo 2772286441Srpaulo return 0; 2773286441Srpaulo } 2774286441Srpaulo 2775303628Ssbruno if ((error = iwm_send_bt_init_conf(sc)) != 0) { 2776303628Ssbruno device_printf(sc->sc_dev, 2777303628Ssbruno "failed to send bt coex configuration: %d\n", error); 2778303628Ssbruno return error; 2779303628Ssbruno } 2780303628Ssbruno 2781303628Ssbruno /* Init Smart FIFO. */ 2782303628Ssbruno error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF); 2783303628Ssbruno if (error != 0) 2784303628Ssbruno return error; 2785303628Ssbruno 2786330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_RESET, 2787330156Seadler "%s: phy_txant=0x%08x, nvm_valid_tx_ant=0x%02x, valid=0x%02x\n", 2788330156Seadler __func__, 2789330156Seadler ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) 2790330156Seadler >> IWM_FW_PHY_CFG_TX_CHAIN_POS), 2791330165Seadler sc->nvm_data->valid_tx_ant, 2792330156Seadler iwm_fw_valid_tx_ant(sc)); 2793330156Seadler 2794330156Seadler 2795286441Srpaulo /* Send TX valid antennas before triggering calibrations */ 2796303628Ssbruno if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { 2797303628Ssbruno device_printf(sc->sc_dev, 2798303628Ssbruno "failed to send antennas before calibration: %d\n", error); 2799286441Srpaulo return error; 2800303628Ssbruno } 2801286441Srpaulo 2802286441Srpaulo /* 2803303628Ssbruno * Send phy configurations command to init uCode 2804303628Ssbruno * to start the 16.0 uCode init image internal calibrations. 2805303628Ssbruno */ 2806286441Srpaulo if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { 2807286441Srpaulo device_printf(sc->sc_dev, 2808286441Srpaulo "%s: failed to run internal calibration: %d\n", 2809286441Srpaulo __func__, error); 2810286441Srpaulo return error; 2811286441Srpaulo } 2812286441Srpaulo 2813286441Srpaulo /* 2814286441Srpaulo * Nothing to do but wait for the init complete notification 2815286441Srpaulo * from the firmware 2816286441Srpaulo */ 2817303628Ssbruno while (!sc->sc_init_complete) { 2818303628Ssbruno error = msleep(&sc->sc_init_complete, &sc->sc_mtx, 2819303628Ssbruno 0, "iwminit", 2*hz); 2820303628Ssbruno if (error) { 2821303628Ssbruno device_printf(sc->sc_dev, "init complete failed: %d\n", 2822303628Ssbruno sc->sc_init_complete); 2823286441Srpaulo break; 2824303628Ssbruno } 2825303628Ssbruno } 2826286441Srpaulo 2827303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, "init %scomplete\n", 2828303628Ssbruno sc->sc_init_complete ? "" : "not "); 2829303628Ssbruno 2830286441Srpaulo return error; 2831286441Srpaulo} 2832286441Srpaulo 2833286441Srpaulo/* 2834286441Srpaulo * receive side 2835286441Srpaulo */ 2836286441Srpaulo 2837286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */ 2838286441Srpaulostatic int 2839286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) 2840286441Srpaulo{ 2841286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 2842286441Srpaulo struct iwm_rx_data *data = &ring->data[idx]; 2843286441Srpaulo struct mbuf *m; 2844301845Sadrian bus_dmamap_t dmamap = NULL; 2845303628Ssbruno bus_dma_segment_t seg; 2846303628Ssbruno int nsegs, error; 2847286441Srpaulo 2848286441Srpaulo m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); 2849286441Srpaulo if (m == NULL) 2850286441Srpaulo return ENOBUFS; 2851286441Srpaulo 2852286441Srpaulo m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; 2853303628Ssbruno error = bus_dmamap_load_mbuf_sg(ring->data_dmat, ring->spare_map, m, 2854303628Ssbruno &seg, &nsegs, BUS_DMA_NOWAIT); 2855303628Ssbruno if (error != 0) { 2856286441Srpaulo device_printf(sc->sc_dev, 2857301845Sadrian "%s: can't map mbuf, error %d\n", __func__, error); 2858286441Srpaulo goto fail; 2859286441Srpaulo } 2860301845Sadrian 2861301845Sadrian if (data->m != NULL) 2862301845Sadrian bus_dmamap_unload(ring->data_dmat, data->map); 2863301845Sadrian 2864301845Sadrian /* Swap ring->spare_map with data->map */ 2865301845Sadrian dmamap = data->map; 2866301845Sadrian data->map = ring->spare_map; 2867301845Sadrian ring->spare_map = dmamap; 2868301845Sadrian 2869286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD); 2870301845Sadrian data->m = m; 2871286441Srpaulo 2872286441Srpaulo /* Update RX descriptor. */ 2873303628Ssbruno KASSERT((seg.ds_addr & 255) == 0, ("seg.ds_addr not aligned")); 2874303628Ssbruno ring->desc[idx] = htole32(seg.ds_addr >> 8); 2875286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2876286441Srpaulo BUS_DMASYNC_PREWRITE); 2877286441Srpaulo 2878286441Srpaulo return 0; 2879286441Srpaulofail: 2880303628Ssbruno m_freem(m); 2881286441Srpaulo return error; 2882286441Srpaulo} 2883286441Srpaulo 2884286441Srpaulo/* iwlwifi: mvm/rx.c */ 2885286441Srpaulo#define IWM_RSSI_OFFSET 50 2886286441Srpaulostatic int 2887286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2888286441Srpaulo{ 2889286441Srpaulo int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; 2890286441Srpaulo uint32_t agc_a, agc_b; 2891286441Srpaulo uint32_t val; 2892286441Srpaulo 2893286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); 2894286441Srpaulo agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; 2895286441Srpaulo agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; 2896286441Srpaulo 2897286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); 2898286441Srpaulo rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; 2899286441Srpaulo rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; 2900286441Srpaulo 2901286441Srpaulo /* 2902286441Srpaulo * dBm = rssi dB - agc dB - constant. 2903286441Srpaulo * Higher AGC (higher radio gain) means lower signal. 2904286441Srpaulo */ 2905286441Srpaulo rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; 2906286441Srpaulo rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; 2907286441Srpaulo max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); 2908286441Srpaulo 2909286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2910286441Srpaulo "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", 2911286441Srpaulo rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); 2912286441Srpaulo 2913286441Srpaulo return max_rssi_dbm; 2914286441Srpaulo} 2915286441Srpaulo 2916286441Srpaulo/* iwlwifi: mvm/rx.c */ 2917286441Srpaulo/* 2918286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API 2919286441Srpaulo * values are reported by the fw as positive values - need to negate 2920286441Srpaulo * to obtain their dBM. Account for missing antennas by replacing 0 2921286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value. 2922286441Srpaulo */ 2923286441Srpaulostatic int 2924286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2925286441Srpaulo{ 2926286441Srpaulo int energy_a, energy_b, energy_c, max_energy; 2927286441Srpaulo uint32_t val; 2928286441Srpaulo 2929286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); 2930286441Srpaulo energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> 2931286441Srpaulo IWM_RX_INFO_ENERGY_ANT_A_POS; 2932286441Srpaulo energy_a = energy_a ? -energy_a : -256; 2933286441Srpaulo energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> 2934286441Srpaulo IWM_RX_INFO_ENERGY_ANT_B_POS; 2935286441Srpaulo energy_b = energy_b ? -energy_b : -256; 2936286441Srpaulo energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> 2937286441Srpaulo IWM_RX_INFO_ENERGY_ANT_C_POS; 2938286441Srpaulo energy_c = energy_c ? -energy_c : -256; 2939286441Srpaulo max_energy = MAX(energy_a, energy_b); 2940286441Srpaulo max_energy = MAX(max_energy, energy_c); 2941286441Srpaulo 2942286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2943286441Srpaulo "energy In A %d B %d C %d , and max %d\n", 2944286441Srpaulo energy_a, energy_b, energy_c, max_energy); 2945286441Srpaulo 2946286441Srpaulo return max_energy; 2947286441Srpaulo} 2948286441Srpaulo 2949286441Srpaulostatic void 2950286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, 2951286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2952286441Srpaulo{ 2953286441Srpaulo struct iwm_rx_phy_info *phy_info = (void *)pkt->data; 2954286441Srpaulo 2955286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n"); 2956286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2957286441Srpaulo 2958286441Srpaulo memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); 2959286441Srpaulo} 2960286441Srpaulo 2961286441Srpaulo/* 2962286441Srpaulo * Retrieve the average noise (in dBm) among receivers. 2963286441Srpaulo */ 2964286441Srpaulostatic int 2965330144Seadleriwm_get_noise(struct iwm_softc *sc, 2966330144Seadler const struct iwm_mvm_statistics_rx_non_phy *stats) 2967286441Srpaulo{ 2968286441Srpaulo int i, total, nbant, noise; 2969286441Srpaulo 2970286441Srpaulo total = nbant = noise = 0; 2971286441Srpaulo for (i = 0; i < 3; i++) { 2972286441Srpaulo noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; 2973330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: i=%d, noise=%d\n", 2974330144Seadler __func__, 2975330144Seadler i, 2976330144Seadler noise); 2977330144Seadler 2978286441Srpaulo if (noise) { 2979286441Srpaulo total += noise; 2980286441Srpaulo nbant++; 2981286441Srpaulo } 2982286441Srpaulo } 2983286441Srpaulo 2984330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, "%s: nbant=%d, total=%d\n", 2985330144Seadler __func__, nbant, total); 2986330144Seadler#if 0 2987286441Srpaulo /* There should be at least one antenna but check anyway. */ 2988286441Srpaulo return (nbant == 0) ? -127 : (total / nbant) - 107; 2989330144Seadler#else 2990330144Seadler /* For now, just hard-code it to -96 to be safe */ 2991330144Seadler return (-96); 2992330144Seadler#endif 2993286441Srpaulo} 2994286441Srpaulo 2995286441Srpaulo/* 2996286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler 2997286441Srpaulo * 2998286441Srpaulo * Handles the actual data of the Rx packet from the fw 2999286441Srpaulo */ 3000286441Srpaulostatic void 3001286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, 3002286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 3003286441Srpaulo{ 3004287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3005286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3006286441Srpaulo struct ieee80211_frame *wh; 3007286441Srpaulo struct ieee80211_node *ni; 3008286441Srpaulo struct ieee80211_rx_stats rxs; 3009286441Srpaulo struct mbuf *m; 3010286441Srpaulo struct iwm_rx_phy_info *phy_info; 3011286441Srpaulo struct iwm_rx_mpdu_res_start *rx_res; 3012286441Srpaulo uint32_t len; 3013286441Srpaulo uint32_t rx_pkt_status; 3014286441Srpaulo int rssi; 3015286441Srpaulo 3016286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 3017286441Srpaulo 3018286441Srpaulo phy_info = &sc->sc_last_phy_info; 3019286441Srpaulo rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; 3020286441Srpaulo wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); 3021286441Srpaulo len = le16toh(rx_res->byte_count); 3022286441Srpaulo rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); 3023286441Srpaulo 3024286441Srpaulo m = data->m; 3025286441Srpaulo m->m_data = pkt->data + sizeof(*rx_res); 3026286441Srpaulo m->m_pkthdr.len = m->m_len = len; 3027286441Srpaulo 3028286441Srpaulo if (__predict_false(phy_info->cfg_phy_cnt > 20)) { 3029286441Srpaulo device_printf(sc->sc_dev, 3030286441Srpaulo "dsp size out of range [0,20]: %d\n", 3031286441Srpaulo phy_info->cfg_phy_cnt); 3032330157Seadler goto fail; 3033286441Srpaulo } 3034286441Srpaulo 3035286441Srpaulo if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || 3036286441Srpaulo !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { 3037286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3038286441Srpaulo "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); 3039330157Seadler goto fail; 3040286441Srpaulo } 3041286441Srpaulo 3042286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { 3043286441Srpaulo rssi = iwm_mvm_get_signal_strength(sc, phy_info); 3044286441Srpaulo } else { 3045286441Srpaulo rssi = iwm_mvm_calc_rssi(sc, phy_info); 3046286441Srpaulo } 3047286441Srpaulo 3048330144Seadler /* Note: RSSI is absolute (ie a -ve value) */ 3049330144Seadler if (rssi < IWM_MIN_DBM) 3050330144Seadler rssi = IWM_MIN_DBM; 3051330144Seadler else if (rssi > IWM_MAX_DBM) 3052330144Seadler rssi = IWM_MAX_DBM; 3053330144Seadler 3054330144Seadler /* Map it to relative value */ 3055330144Seadler rssi = rssi - sc->sc_noise; 3056330144Seadler 3057286441Srpaulo /* replenish ring for the buffer we're going to feed to the sharks */ 3058286441Srpaulo if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) { 3059286441Srpaulo device_printf(sc->sc_dev, "%s: unable to add more buffers\n", 3060286441Srpaulo __func__); 3061330157Seadler goto fail; 3062286441Srpaulo } 3063286441Srpaulo 3064330144Seadler IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3065330144Seadler "%s: rssi=%d, noise=%d\n", __func__, rssi, sc->sc_noise); 3066330144Seadler 3067286441Srpaulo ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 3068286441Srpaulo 3069286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 3070286441Srpaulo "%s: phy_info: channel=%d, flags=0x%08x\n", 3071286441Srpaulo __func__, 3072286441Srpaulo le16toh(phy_info->channel), 3073286441Srpaulo le16toh(phy_info->phy_flags)); 3074286441Srpaulo 3075286441Srpaulo /* 3076286441Srpaulo * Populate an RX state struct with the provided information. 3077286441Srpaulo */ 3078286441Srpaulo bzero(&rxs, sizeof(rxs)); 3079286441Srpaulo rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; 3080286441Srpaulo rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; 3081286441Srpaulo rxs.c_ieee = le16toh(phy_info->channel); 3082286441Srpaulo if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) { 3083286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); 3084286441Srpaulo } else { 3085286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ); 3086286441Srpaulo } 3087330144Seadler 3088330144Seadler /* rssi is in 1/2db units */ 3089330144Seadler rxs.rssi = rssi * 2; 3090286441Srpaulo rxs.nf = sc->sc_noise; 3091286441Srpaulo 3092286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3093286441Srpaulo struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; 3094286441Srpaulo 3095286441Srpaulo tap->wr_flags = 0; 3096286441Srpaulo if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) 3097286441Srpaulo tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 3098286441Srpaulo tap->wr_chan_freq = htole16(rxs.c_freq); 3099286441Srpaulo /* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */ 3100286441Srpaulo tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); 3101286441Srpaulo tap->wr_dbm_antsignal = (int8_t)rssi; 3102286441Srpaulo tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; 3103286441Srpaulo tap->wr_tsft = phy_info->system_timestamp; 3104286441Srpaulo switch (phy_info->rate) { 3105286441Srpaulo /* CCK rates. */ 3106286441Srpaulo case 10: tap->wr_rate = 2; break; 3107286441Srpaulo case 20: tap->wr_rate = 4; break; 3108286441Srpaulo case 55: tap->wr_rate = 11; break; 3109286441Srpaulo case 110: tap->wr_rate = 22; break; 3110286441Srpaulo /* OFDM rates. */ 3111286441Srpaulo case 0xd: tap->wr_rate = 12; break; 3112286441Srpaulo case 0xf: tap->wr_rate = 18; break; 3113286441Srpaulo case 0x5: tap->wr_rate = 24; break; 3114286441Srpaulo case 0x7: tap->wr_rate = 36; break; 3115286441Srpaulo case 0x9: tap->wr_rate = 48; break; 3116286441Srpaulo case 0xb: tap->wr_rate = 72; break; 3117286441Srpaulo case 0x1: tap->wr_rate = 96; break; 3118286441Srpaulo case 0x3: tap->wr_rate = 108; break; 3119286441Srpaulo /* Unknown rate: should not happen. */ 3120286441Srpaulo default: tap->wr_rate = 0; 3121286441Srpaulo } 3122286441Srpaulo } 3123286441Srpaulo 3124286441Srpaulo IWM_UNLOCK(sc); 3125286441Srpaulo if (ni != NULL) { 3126286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m); 3127286441Srpaulo ieee80211_input_mimo(ni, m, &rxs); 3128286441Srpaulo ieee80211_free_node(ni); 3129286441Srpaulo } else { 3130286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m); 3131286441Srpaulo ieee80211_input_mimo_all(ic, m, &rxs); 3132286441Srpaulo } 3133286441Srpaulo IWM_LOCK(sc); 3134330157Seadler 3135330157Seadler return; 3136330157Seadler 3137330157Seadlerfail: counter_u64_add(ic->ic_ierrors, 1); 3138286441Srpaulo} 3139286441Srpaulo 3140293100Savosstatic int 3141286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, 3142286441Srpaulo struct iwm_node *in) 3143286441Srpaulo{ 3144286441Srpaulo struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; 3145293100Savos struct ieee80211_node *ni = &in->in_ni; 3146293100Savos struct ieee80211vap *vap = ni->ni_vap; 3147286441Srpaulo int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; 3148286441Srpaulo int failack = tx_resp->failure_frame; 3149286441Srpaulo 3150286441Srpaulo KASSERT(tx_resp->frame_count == 1, ("too many frames")); 3151286441Srpaulo 3152286441Srpaulo /* Update rate control statistics. */ 3153298611Sadrian 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", 3154298611Sadrian __func__, 3155298611Sadrian (int) le16toh(tx_resp->status.status), 3156298611Sadrian (int) le16toh(tx_resp->status.sequence), 3157298611Sadrian tx_resp->frame_count, 3158298611Sadrian tx_resp->bt_kill_count, 3159298611Sadrian tx_resp->failure_rts, 3160298611Sadrian tx_resp->failure_frame, 3161298611Sadrian le32toh(tx_resp->initial_rate), 3162298611Sadrian (int) le16toh(tx_resp->wireless_media_time)); 3163298611Sadrian 3164286441Srpaulo if (status != IWM_TX_STATUS_SUCCESS && 3165286441Srpaulo status != IWM_TX_STATUS_DIRECT_DONE) { 3166293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3167286441Srpaulo IEEE80211_RATECTL_TX_FAILURE, &failack, NULL); 3168293100Savos return (1); 3169286441Srpaulo } else { 3170293100Savos ieee80211_ratectl_tx_complete(vap, ni, 3171286441Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL); 3172293100Savos return (0); 3173286441Srpaulo } 3174286441Srpaulo} 3175286441Srpaulo 3176286441Srpaulostatic void 3177286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc, 3178286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 3179286441Srpaulo{ 3180286441Srpaulo struct iwm_cmd_header *cmd_hdr = &pkt->hdr; 3181286441Srpaulo int idx = cmd_hdr->idx; 3182286441Srpaulo int qid = cmd_hdr->qid; 3183286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[qid]; 3184286441Srpaulo struct iwm_tx_data *txd = &ring->data[idx]; 3185286441Srpaulo struct iwm_node *in = txd->in; 3186293100Savos struct mbuf *m = txd->m; 3187293100Savos int status; 3188286441Srpaulo 3189293100Savos KASSERT(txd->done == 0, ("txd not done")); 3190293100Savos KASSERT(txd->in != NULL, ("txd without node")); 3191293100Savos KASSERT(txd->m != NULL, ("txd without mbuf")); 3192293100Savos 3193286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 3194286441Srpaulo 3195286441Srpaulo sc->sc_tx_timer = 0; 3196286441Srpaulo 3197293100Savos status = iwm_mvm_rx_tx_cmd_single(sc, pkt, in); 3198286441Srpaulo 3199286441Srpaulo /* Unmap and free mbuf. */ 3200286441Srpaulo bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE); 3201286441Srpaulo bus_dmamap_unload(ring->data_dmat, txd->map); 3202286441Srpaulo 3203286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3204286441Srpaulo "free txd %p, in %p\n", txd, txd->in); 3205286441Srpaulo txd->done = 1; 3206286441Srpaulo txd->m = NULL; 3207286441Srpaulo txd->in = NULL; 3208286441Srpaulo 3209293100Savos ieee80211_tx_complete(&in->in_ni, m, status); 3210293100Savos 3211286441Srpaulo if (--ring->queued < IWM_TX_RING_LOMARK) { 3212286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 3213287197Sglebius if (sc->qfullmsk == 0) { 3214287197Sglebius iwm_start(sc); 3215286441Srpaulo } 3216286441Srpaulo } 3217286441Srpaulo} 3218286441Srpaulo 3219286441Srpaulo/* 3220286441Srpaulo * transmit side 3221286441Srpaulo */ 3222286441Srpaulo 3223286441Srpaulo/* 3224286441Srpaulo * Process a "command done" firmware notification. This is where we wakeup 3225286441Srpaulo * processes waiting for a synchronous command completion. 3226286441Srpaulo * from if_iwn 3227286441Srpaulo */ 3228286441Srpaulostatic void 3229286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 3230286441Srpaulo{ 3231286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; 3232286441Srpaulo struct iwm_tx_data *data; 3233286441Srpaulo 3234286441Srpaulo if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { 3235286441Srpaulo return; /* Not a command ack. */ 3236286441Srpaulo } 3237286441Srpaulo 3238330142Seadler /* XXX wide commands? */ 3239330142Seadler IWM_DPRINTF(sc, IWM_DEBUG_CMD, 3240330142Seadler "cmd notification type 0x%x qid %d idx %d\n", 3241330142Seadler pkt->hdr.code, pkt->hdr.qid, pkt->hdr.idx); 3242330142Seadler 3243286441Srpaulo data = &ring->data[pkt->hdr.idx]; 3244286441Srpaulo 3245286441Srpaulo /* If the command was mapped in an mbuf, free it. */ 3246286441Srpaulo if (data->m != NULL) { 3247286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3248286441Srpaulo BUS_DMASYNC_POSTWRITE); 3249286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 3250286441Srpaulo m_freem(data->m); 3251286441Srpaulo data->m = NULL; 3252286441Srpaulo } 3253286441Srpaulo wakeup(&ring->desc[pkt->hdr.idx]); 3254286441Srpaulo} 3255286441Srpaulo 3256286441Srpaulo#if 0 3257286441Srpaulo/* 3258286441Srpaulo * necessary only for block ack mode 3259286441Srpaulo */ 3260286441Srpaulovoid 3261286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, 3262286441Srpaulo uint16_t len) 3263286441Srpaulo{ 3264286441Srpaulo struct iwm_agn_scd_bc_tbl *scd_bc_tbl; 3265286441Srpaulo uint16_t w_val; 3266286441Srpaulo 3267286441Srpaulo scd_bc_tbl = sc->sched_dma.vaddr; 3268286441Srpaulo 3269286441Srpaulo len += 8; /* magic numbers came naturally from paris */ 3270286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) 3271286441Srpaulo len = roundup(len, 4) / 4; 3272286441Srpaulo 3273286441Srpaulo w_val = htole16(sta_id << 12 | len); 3274286441Srpaulo 3275286441Srpaulo /* Update TX scheduler. */ 3276286441Srpaulo scd_bc_tbl[qid].tfd_offset[idx] = w_val; 3277286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3278286441Srpaulo BUS_DMASYNC_PREWRITE); 3279286441Srpaulo 3280286441Srpaulo /* I really wonder what this is ?!? */ 3281286441Srpaulo if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { 3282286441Srpaulo scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; 3283286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 3284286441Srpaulo BUS_DMASYNC_PREWRITE); 3285286441Srpaulo } 3286286441Srpaulo} 3287286441Srpaulo#endif 3288286441Srpaulo 3289286441Srpaulo/* 3290286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate 3291286441Srpaulo * table entry. return the index into in_ridx[]. 3292286441Srpaulo * 3293286441Srpaulo * The caller then uses that index back into in_ridx 3294286441Srpaulo * to figure out the rate index programmed /into/ 3295286441Srpaulo * the firmware for this given node. 3296286441Srpaulo */ 3297286441Srpaulostatic int 3298286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, 3299286441Srpaulo uint8_t rate) 3300286441Srpaulo{ 3301286441Srpaulo int i; 3302286441Srpaulo uint8_t r; 3303286441Srpaulo 3304286441Srpaulo for (i = 0; i < nitems(in->in_ridx); i++) { 3305286441Srpaulo r = iwm_rates[in->in_ridx[i]].rate; 3306286441Srpaulo if (rate == r) 3307286441Srpaulo return (i); 3308286441Srpaulo } 3309330156Seadler 3310330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3311330156Seadler "%s: couldn't find an entry for rate=%d\n", 3312330156Seadler __func__, 3313330156Seadler rate); 3314330156Seadler 3315286441Srpaulo /* XXX Return the first */ 3316286441Srpaulo /* XXX TODO: have it return the /lowest/ */ 3317286441Srpaulo return (0); 3318286441Srpaulo} 3319286441Srpaulo 3320330156Seadlerstatic int 3321330156Seadleriwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate) 3322330156Seadler{ 3323330156Seadler int i; 3324330156Seadler 3325330156Seadler for (i = 0; i < nitems(iwm_rates); i++) { 3326330156Seadler if (iwm_rates[i].rate == rate) 3327330156Seadler return (i); 3328330156Seadler } 3329330156Seadler /* XXX error? */ 3330330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3331330156Seadler "%s: couldn't find an entry for rate=%d\n", 3332330156Seadler __func__, 3333330156Seadler rate); 3334330156Seadler return (0); 3335330156Seadler} 3336330156Seadler 3337286441Srpaulo/* 3338298875Sadrian * Fill in the rate related information for a transmit command. 3339286441Srpaulo */ 3340286441Srpaulostatic const struct iwm_rate * 3341286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, 3342330155Seadler struct mbuf *m, struct iwm_tx_cmd *tx) 3343286441Srpaulo{ 3344286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 3345330155Seadler struct ieee80211_frame *wh; 3346330155Seadler const struct ieee80211_txparam *tp = ni->ni_txparms; 3347286441Srpaulo const struct iwm_rate *rinfo; 3348330155Seadler int type; 3349330156Seadler int ridx, rate_flags; 3350286441Srpaulo 3351330155Seadler wh = mtod(m, struct ieee80211_frame *); 3352330155Seadler type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3353330155Seadler 3354286441Srpaulo tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; 3355286441Srpaulo tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; 3356286441Srpaulo 3357330155Seadler if (type == IEEE80211_FC0_TYPE_MGT) { 3358330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3359330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3360330156Seadler "%s: MGT (%d)\n", __func__, tp->mgmtrate); 3361330155Seadler } else if (IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3362330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mcastrate); 3363330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3364330156Seadler "%s: MCAST (%d)\n", __func__, tp->mcastrate); 3365330155Seadler } else if (tp->ucastrate != IEEE80211_FIXED_RATE_NONE) { 3366330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->ucastrate); 3367330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3368330156Seadler "%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate); 3369330155Seadler } else if (m->m_flags & M_EAPOL) { 3370330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3371330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3372330156Seadler "%s: EAPOL\n", __func__); 3373330156Seadler } else if (type == IEEE80211_FC0_TYPE_DATA) { 3374330156Seadler int i; 3375330156Seadler 3376286441Srpaulo /* for data frames, use RS table */ 3377330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__); 3378330155Seadler /* XXX pass pktlen */ 3379286441Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 3380286441Srpaulo i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); 3381286441Srpaulo ridx = in->in_ridx[i]; 3382286441Srpaulo 3383286441Srpaulo /* This is the index into the programmed table */ 3384286441Srpaulo tx->initial_rate_index = i; 3385286441Srpaulo tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); 3386330156Seadler 3387286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3388286441Srpaulo "%s: start with i=%d, txrate %d\n", 3389286441Srpaulo __func__, i, iwm_rates[ridx].rate); 3390330156Seadler } else { 3391330156Seadler ridx = iwm_tx_rateidx_global_lookup(sc, tp->mgmtrate); 3392330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DEFAULT (%d)\n", 3393330156Seadler __func__, tp->mgmtrate); 3394286441Srpaulo } 3395286441Srpaulo 3396330156Seadler IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 3397330156Seadler "%s: frame type=%d txrate %d\n", 3398330156Seadler __func__, type, iwm_rates[ridx].rate); 3399330156Seadler 3400286441Srpaulo rinfo = &iwm_rates[ridx]; 3401286441Srpaulo 3402286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n", 3403286441Srpaulo __func__, ridx, 3404286441Srpaulo rinfo->rate, 3405286441Srpaulo !! (IWM_RIDX_IS_CCK(ridx)) 3406286441Srpaulo ); 3407286441Srpaulo 3408286441Srpaulo /* XXX TODO: hard-coded TX antenna? */ 3409286441Srpaulo rate_flags = 1 << IWM_RATE_MCS_ANT_POS; 3410286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 3411286441Srpaulo rate_flags |= IWM_RATE_MCS_CCK_MSK; 3412286441Srpaulo tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); 3413286441Srpaulo 3414286441Srpaulo return rinfo; 3415286441Srpaulo} 3416286441Srpaulo 3417286441Srpaulo#define TB0_SIZE 16 3418286441Srpaulostatic int 3419286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) 3420286441Srpaulo{ 3421287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3422286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 3423293099Savos struct iwm_node *in = IWM_NODE(ni); 3424286441Srpaulo struct iwm_tx_ring *ring; 3425286441Srpaulo struct iwm_tx_data *data; 3426286441Srpaulo struct iwm_tfd *desc; 3427286441Srpaulo struct iwm_device_cmd *cmd; 3428286441Srpaulo struct iwm_tx_cmd *tx; 3429286441Srpaulo struct ieee80211_frame *wh; 3430286441Srpaulo struct ieee80211_key *k = NULL; 3431286441Srpaulo struct mbuf *m1; 3432286441Srpaulo const struct iwm_rate *rinfo; 3433286441Srpaulo uint32_t flags; 3434286441Srpaulo u_int hdrlen; 3435286441Srpaulo bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER]; 3436286441Srpaulo int nsegs; 3437286441Srpaulo uint8_t tid, type; 3438286441Srpaulo int i, totlen, error, pad; 3439286441Srpaulo 3440286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3441286441Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 3442286441Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 3443286441Srpaulo tid = 0; 3444286441Srpaulo ring = &sc->txq[ac]; 3445286441Srpaulo desc = &ring->desc[ring->cur]; 3446286441Srpaulo memset(desc, 0, sizeof(*desc)); 3447286441Srpaulo data = &ring->data[ring->cur]; 3448286441Srpaulo 3449286441Srpaulo /* Fill out iwm_tx_cmd to send to the firmware */ 3450286441Srpaulo cmd = &ring->cmd[ring->cur]; 3451286441Srpaulo cmd->hdr.code = IWM_TX_CMD; 3452286441Srpaulo cmd->hdr.flags = 0; 3453286441Srpaulo cmd->hdr.qid = ring->qid; 3454286441Srpaulo cmd->hdr.idx = ring->cur; 3455286441Srpaulo 3456286441Srpaulo tx = (void *)cmd->data; 3457286441Srpaulo memset(tx, 0, sizeof(*tx)); 3458286441Srpaulo 3459330155Seadler rinfo = iwm_tx_fill_cmd(sc, in, m, tx); 3460286441Srpaulo 3461286441Srpaulo /* Encrypt the frame if need be. */ 3462286441Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 3463286441Srpaulo /* Retrieve key for TX && do software encryption. */ 3464286441Srpaulo k = ieee80211_crypto_encap(ni, m); 3465286441Srpaulo if (k == NULL) { 3466286441Srpaulo m_freem(m); 3467286441Srpaulo return (ENOBUFS); 3468286441Srpaulo } 3469286441Srpaulo /* 802.11 header may have moved. */ 3470286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 3471286441Srpaulo } 3472286441Srpaulo 3473286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 3474286441Srpaulo struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; 3475286441Srpaulo 3476286441Srpaulo tap->wt_flags = 0; 3477286441Srpaulo tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); 3478286441Srpaulo tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); 3479286441Srpaulo tap->wt_rate = rinfo->rate; 3480286441Srpaulo if (k != NULL) 3481286441Srpaulo tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 3482286441Srpaulo ieee80211_radiotap_tx(vap, m); 3483286441Srpaulo } 3484286441Srpaulo 3485286441Srpaulo 3486286441Srpaulo totlen = m->m_pkthdr.len; 3487286441Srpaulo 3488286441Srpaulo flags = 0; 3489286441Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3490286441Srpaulo flags |= IWM_TX_CMD_FLG_ACK; 3491286441Srpaulo } 3492286441Srpaulo 3493303628Ssbruno if (type == IEEE80211_FC0_TYPE_DATA 3494286441Srpaulo && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) 3495286441Srpaulo && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 3496286441Srpaulo flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; 3497286441Srpaulo } 3498286441Srpaulo 3499286441Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 3500286441Srpaulo type != IEEE80211_FC0_TYPE_DATA) 3501286441Srpaulo tx->sta_id = sc->sc_aux_sta.sta_id; 3502286441Srpaulo else 3503286441Srpaulo tx->sta_id = IWM_STATION_ID; 3504286441Srpaulo 3505286441Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 3506286441Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 3507286441Srpaulo 3508286441Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 3509303628Ssbruno subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) { 3510303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_ASSOC); 3511303628Ssbruno } else if (subtype == IEEE80211_FC0_SUBTYPE_ACTION) { 3512303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3513303628Ssbruno } else { 3514303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_MGMT); 3515303628Ssbruno } 3516286441Srpaulo } else { 3517303628Ssbruno tx->pm_frame_timeout = htole16(IWM_PM_FRAME_NONE); 3518286441Srpaulo } 3519286441Srpaulo 3520286441Srpaulo if (hdrlen & 3) { 3521286441Srpaulo /* First segment length must be a multiple of 4. */ 3522286441Srpaulo flags |= IWM_TX_CMD_FLG_MH_PAD; 3523286441Srpaulo pad = 4 - (hdrlen & 3); 3524286441Srpaulo } else 3525286441Srpaulo pad = 0; 3526286441Srpaulo 3527286441Srpaulo tx->driver_txop = 0; 3528286441Srpaulo tx->next_frame_len = 0; 3529286441Srpaulo 3530286441Srpaulo tx->len = htole16(totlen); 3531286441Srpaulo tx->tid_tspec = tid; 3532286441Srpaulo tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); 3533286441Srpaulo 3534286441Srpaulo /* Set physical address of "scratch area". */ 3535286441Srpaulo tx->dram_lsb_ptr = htole32(data->scratch_paddr); 3536286441Srpaulo tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); 3537286441Srpaulo 3538286441Srpaulo /* Copy 802.11 header in TX command. */ 3539286441Srpaulo memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); 3540286441Srpaulo 3541286441Srpaulo flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; 3542286441Srpaulo 3543286441Srpaulo tx->sec_ctl = 0; 3544286441Srpaulo tx->tx_flags |= htole32(flags); 3545286441Srpaulo 3546286441Srpaulo /* Trim 802.11 header. */ 3547286441Srpaulo m_adj(m, hdrlen); 3548286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3549286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3550286441Srpaulo if (error != 0) { 3551286441Srpaulo if (error != EFBIG) { 3552286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3553286441Srpaulo error); 3554286441Srpaulo m_freem(m); 3555286441Srpaulo return error; 3556286441Srpaulo } 3557286441Srpaulo /* Too many DMA segments, linearize mbuf. */ 3558293119Savos m1 = m_collapse(m, M_NOWAIT, IWM_MAX_SCATTER - 2); 3559286441Srpaulo if (m1 == NULL) { 3560293119Savos device_printf(sc->sc_dev, 3561293119Savos "%s: could not defrag mbuf\n", __func__); 3562286441Srpaulo m_freem(m); 3563293119Savos return (ENOBUFS); 3564286441Srpaulo } 3565286441Srpaulo m = m1; 3566293119Savos 3567286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 3568286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 3569286441Srpaulo if (error != 0) { 3570286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 3571286441Srpaulo error); 3572286441Srpaulo m_freem(m); 3573286441Srpaulo return error; 3574286441Srpaulo } 3575286441Srpaulo } 3576286441Srpaulo data->m = m; 3577286441Srpaulo data->in = in; 3578286441Srpaulo data->done = 0; 3579286441Srpaulo 3580286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3581286441Srpaulo "sending txd %p, in %p\n", data, data->in); 3582286441Srpaulo KASSERT(data->in != NULL, ("node is NULL")); 3583286441Srpaulo 3584286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3585303628Ssbruno "sending data: qid=%d idx=%d len=%d nsegs=%d txflags=0x%08x rate_n_flags=0x%08x rateidx=%u\n", 3586298611Sadrian ring->qid, ring->cur, totlen, nsegs, 3587298611Sadrian le32toh(tx->tx_flags), 3588298611Sadrian le32toh(tx->rate_n_flags), 3589303628Ssbruno tx->initial_rate_index 3590298611Sadrian ); 3591286441Srpaulo 3592286441Srpaulo /* Fill TX descriptor. */ 3593286441Srpaulo desc->num_tbs = 2 + nsegs; 3594286441Srpaulo 3595286441Srpaulo desc->tbs[0].lo = htole32(data->cmd_paddr); 3596286441Srpaulo desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3597286441Srpaulo (TB0_SIZE << 4); 3598286441Srpaulo desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); 3599286441Srpaulo desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 3600286441Srpaulo ((sizeof(struct iwm_cmd_header) + sizeof(*tx) 3601286441Srpaulo + hdrlen + pad - TB0_SIZE) << 4); 3602286441Srpaulo 3603286441Srpaulo /* Other DMA segments are for data payload. */ 3604286441Srpaulo for (i = 0; i < nsegs; i++) { 3605286441Srpaulo seg = &segs[i]; 3606286441Srpaulo desc->tbs[i+2].lo = htole32(seg->ds_addr); 3607286441Srpaulo desc->tbs[i+2].hi_n_len = \ 3608286441Srpaulo htole16(iwm_get_dma_hi_addr(seg->ds_addr)) 3609286441Srpaulo | ((seg->ds_len) << 4); 3610286441Srpaulo } 3611286441Srpaulo 3612286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 3613286441Srpaulo BUS_DMASYNC_PREWRITE); 3614286441Srpaulo bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, 3615286441Srpaulo BUS_DMASYNC_PREWRITE); 3616286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 3617286441Srpaulo BUS_DMASYNC_PREWRITE); 3618286441Srpaulo 3619286441Srpaulo#if 0 3620286441Srpaulo iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); 3621286441Srpaulo#endif 3622286441Srpaulo 3623286441Srpaulo /* Kick TX ring. */ 3624286441Srpaulo ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; 3625286441Srpaulo IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 3626286441Srpaulo 3627286441Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 3628286441Srpaulo if (++ring->queued > IWM_TX_RING_HIMARK) { 3629286441Srpaulo sc->qfullmsk |= 1 << ring->qid; 3630286441Srpaulo } 3631286441Srpaulo 3632286441Srpaulo return 0; 3633286441Srpaulo} 3634286441Srpaulo 3635286441Srpaulostatic int 3636286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 3637286441Srpaulo const struct ieee80211_bpf_params *params) 3638286441Srpaulo{ 3639286441Srpaulo struct ieee80211com *ic = ni->ni_ic; 3640286865Sadrian struct iwm_softc *sc = ic->ic_softc; 3641286441Srpaulo int error = 0; 3642286441Srpaulo 3643286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3644286441Srpaulo "->%s begin\n", __func__); 3645286441Srpaulo 3646287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 3647286441Srpaulo m_freem(m); 3648286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 3649286441Srpaulo "<-%s not RUNNING\n", __func__); 3650286441Srpaulo return (ENETDOWN); 3651286441Srpaulo } 3652286441Srpaulo 3653286441Srpaulo IWM_LOCK(sc); 3654286441Srpaulo /* XXX fix this */ 3655286441Srpaulo if (params == NULL) { 3656286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3657286441Srpaulo } else { 3658286441Srpaulo error = iwm_tx(sc, m, ni, 0); 3659286441Srpaulo } 3660286441Srpaulo sc->sc_tx_timer = 5; 3661286441Srpaulo IWM_UNLOCK(sc); 3662286441Srpaulo 3663286441Srpaulo return (error); 3664286441Srpaulo} 3665286441Srpaulo 3666286441Srpaulo/* 3667286441Srpaulo * mvm/tx.c 3668286441Srpaulo */ 3669286441Srpaulo 3670286441Srpaulo/* 3671286441Srpaulo * Note that there are transports that buffer frames before they reach 3672286441Srpaulo * the firmware. This means that after flush_tx_path is called, the 3673286441Srpaulo * queue might not be empty. The race-free way to handle this is to: 3674286441Srpaulo * 1) set the station as draining 3675286441Srpaulo * 2) flush the Tx path 3676286441Srpaulo * 3) wait for the transport queues to be empty 3677286441Srpaulo */ 3678286441Srpauloint 3679330154Seadleriwm_mvm_flush_tx_path(struct iwm_softc *sc, uint32_t tfd_msk, uint32_t flags) 3680286441Srpaulo{ 3681330154Seadler int ret; 3682286441Srpaulo struct iwm_tx_path_flush_cmd flush_cmd = { 3683286441Srpaulo .queues_ctl = htole32(tfd_msk), 3684286441Srpaulo .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), 3685286441Srpaulo }; 3686286441Srpaulo 3687330154Seadler ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, flags, 3688286441Srpaulo sizeof(flush_cmd), &flush_cmd); 3689286441Srpaulo if (ret) 3690286441Srpaulo device_printf(sc->sc_dev, 3691286441Srpaulo "Flushing tx queue failed: %d\n", ret); 3692286441Srpaulo return ret; 3693286441Srpaulo} 3694286441Srpaulo 3695286441Srpaulo/* 3696286441Srpaulo * BEGIN mvm/sta.c 3697286441Srpaulo */ 3698286441Srpaulo 3699286441Srpaulostatic int 3700286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, 3701303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status) 3702286441Srpaulo{ 3703303628Ssbruno return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd), 3704303628Ssbruno cmd, status); 3705286441Srpaulo} 3706286441Srpaulo 3707286441Srpaulo/* send station add/update command to firmware */ 3708286441Srpaulostatic int 3709286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) 3710286441Srpaulo{ 3711303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 add_sta_cmd; 3712286441Srpaulo int ret; 3713286441Srpaulo uint32_t status; 3714286441Srpaulo 3715286441Srpaulo memset(&add_sta_cmd, 0, sizeof(add_sta_cmd)); 3716286441Srpaulo 3717286441Srpaulo add_sta_cmd.sta_id = IWM_STATION_ID; 3718286441Srpaulo add_sta_cmd.mac_id_n_color 3719286441Srpaulo = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, 3720286441Srpaulo IWM_DEFAULT_COLOR)); 3721286441Srpaulo if (!update) { 3722303628Ssbruno int ac; 3723303628Ssbruno for (ac = 0; ac < WME_NUM_AC; ac++) { 3724303628Ssbruno add_sta_cmd.tfd_queue_msk |= 3725303628Ssbruno htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); 3726303628Ssbruno } 3727286441Srpaulo IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); 3728286441Srpaulo } 3729286441Srpaulo add_sta_cmd.add_modify = update ? 1 : 0; 3730286441Srpaulo add_sta_cmd.station_flags_msk 3731286441Srpaulo |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); 3732303628Ssbruno add_sta_cmd.tid_disable_tx = htole16(0xffff); 3733303628Ssbruno if (update) 3734303628Ssbruno add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX); 3735286441Srpaulo 3736286441Srpaulo status = IWM_ADD_STA_SUCCESS; 3737286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status); 3738286441Srpaulo if (ret) 3739286441Srpaulo return ret; 3740286441Srpaulo 3741286441Srpaulo switch (status) { 3742286441Srpaulo case IWM_ADD_STA_SUCCESS: 3743286441Srpaulo break; 3744286441Srpaulo default: 3745286441Srpaulo ret = EIO; 3746286441Srpaulo device_printf(sc->sc_dev, "IWM_ADD_STA failed\n"); 3747286441Srpaulo break; 3748286441Srpaulo } 3749286441Srpaulo 3750286441Srpaulo return ret; 3751286441Srpaulo} 3752286441Srpaulo 3753286441Srpaulostatic int 3754286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) 3755286441Srpaulo{ 3756301192Sadrian return iwm_mvm_sta_send_to_fw(sc, in, 0); 3757286441Srpaulo} 3758286441Srpaulo 3759286441Srpaulostatic int 3760286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) 3761286441Srpaulo{ 3762286441Srpaulo return iwm_mvm_sta_send_to_fw(sc, in, 1); 3763286441Srpaulo} 3764286441Srpaulo 3765286441Srpaulostatic int 3766286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, 3767286441Srpaulo const uint8_t *addr, uint16_t mac_id, uint16_t color) 3768286441Srpaulo{ 3769303628Ssbruno struct iwm_mvm_add_sta_cmd_v7 cmd; 3770286441Srpaulo int ret; 3771286441Srpaulo uint32_t status; 3772286441Srpaulo 3773286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3774286441Srpaulo cmd.sta_id = sta->sta_id; 3775286441Srpaulo cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); 3776286441Srpaulo 3777286441Srpaulo cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); 3778303628Ssbruno cmd.tid_disable_tx = htole16(0xffff); 3779286441Srpaulo 3780286441Srpaulo if (addr) 3781286441Srpaulo IEEE80211_ADDR_COPY(cmd.addr, addr); 3782286441Srpaulo 3783286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); 3784286441Srpaulo if (ret) 3785286441Srpaulo return ret; 3786286441Srpaulo 3787286441Srpaulo switch (status) { 3788286441Srpaulo case IWM_ADD_STA_SUCCESS: 3789286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 3790286441Srpaulo "%s: Internal station added.\n", __func__); 3791286441Srpaulo return 0; 3792286441Srpaulo default: 3793286441Srpaulo device_printf(sc->sc_dev, 3794286441Srpaulo "%s: Add internal station failed, status=0x%x\n", 3795286441Srpaulo __func__, status); 3796286441Srpaulo ret = EIO; 3797286441Srpaulo break; 3798286441Srpaulo } 3799286441Srpaulo return ret; 3800286441Srpaulo} 3801286441Srpaulo 3802286441Srpaulostatic int 3803286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc) 3804286441Srpaulo{ 3805286441Srpaulo int ret; 3806286441Srpaulo 3807303628Ssbruno sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID; 3808303628Ssbruno sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE); 3809286441Srpaulo 3810303628Ssbruno ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST); 3811303628Ssbruno if (ret) 3812303628Ssbruno return ret; 3813303628Ssbruno 3814286441Srpaulo ret = iwm_mvm_add_int_sta_common(sc, 3815286441Srpaulo &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); 3816286441Srpaulo 3817286441Srpaulo if (ret) 3818286441Srpaulo memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta)); 3819286441Srpaulo return ret; 3820286441Srpaulo} 3821286441Srpaulo 3822286441Srpaulo/* 3823286441Srpaulo * END mvm/sta.c 3824286441Srpaulo */ 3825286441Srpaulo 3826286441Srpaulo/* 3827286441Srpaulo * BEGIN mvm/quota.c 3828286441Srpaulo */ 3829286441Srpaulo 3830286441Srpaulostatic int 3831286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in) 3832286441Srpaulo{ 3833286441Srpaulo struct iwm_time_quota_cmd cmd; 3834286441Srpaulo int i, idx, ret, num_active_macs, quota, quota_rem; 3835286441Srpaulo int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, }; 3836286441Srpaulo int n_ifs[IWM_MAX_BINDINGS] = {0, }; 3837286441Srpaulo uint16_t id; 3838286441Srpaulo 3839286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3840286441Srpaulo 3841286441Srpaulo /* currently, PHY ID == binding ID */ 3842286441Srpaulo if (in) { 3843286441Srpaulo id = in->in_phyctxt->id; 3844286441Srpaulo KASSERT(id < IWM_MAX_BINDINGS, ("invalid id")); 3845286441Srpaulo colors[id] = in->in_phyctxt->color; 3846286441Srpaulo 3847286441Srpaulo if (1) 3848286441Srpaulo n_ifs[id] = 1; 3849286441Srpaulo } 3850286441Srpaulo 3851286441Srpaulo /* 3852286441Srpaulo * The FW's scheduling session consists of 3853286441Srpaulo * IWM_MVM_MAX_QUOTA fragments. Divide these fragments 3854286441Srpaulo * equally between all the bindings that require quota 3855286441Srpaulo */ 3856286441Srpaulo num_active_macs = 0; 3857286441Srpaulo for (i = 0; i < IWM_MAX_BINDINGS; i++) { 3858286441Srpaulo cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID); 3859286441Srpaulo num_active_macs += n_ifs[i]; 3860286441Srpaulo } 3861286441Srpaulo 3862286441Srpaulo quota = 0; 3863286441Srpaulo quota_rem = 0; 3864286441Srpaulo if (num_active_macs) { 3865286441Srpaulo quota = IWM_MVM_MAX_QUOTA / num_active_macs; 3866286441Srpaulo quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs; 3867286441Srpaulo } 3868286441Srpaulo 3869286441Srpaulo for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) { 3870286441Srpaulo if (colors[i] < 0) 3871286441Srpaulo continue; 3872286441Srpaulo 3873286441Srpaulo cmd.quotas[idx].id_and_color = 3874286441Srpaulo htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i])); 3875286441Srpaulo 3876286441Srpaulo if (n_ifs[i] <= 0) { 3877286441Srpaulo cmd.quotas[idx].quota = htole32(0); 3878286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3879286441Srpaulo } else { 3880286441Srpaulo cmd.quotas[idx].quota = htole32(quota * n_ifs[i]); 3881286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3882286441Srpaulo } 3883286441Srpaulo idx++; 3884286441Srpaulo } 3885286441Srpaulo 3886286441Srpaulo /* Give the remainder of the session to the first binding */ 3887286441Srpaulo cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem); 3888286441Srpaulo 3889286441Srpaulo ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC, 3890286441Srpaulo sizeof(cmd), &cmd); 3891286441Srpaulo if (ret) 3892286441Srpaulo device_printf(sc->sc_dev, 3893286441Srpaulo "%s: Failed to send quota: %d\n", __func__, ret); 3894286441Srpaulo return ret; 3895286441Srpaulo} 3896286441Srpaulo 3897286441Srpaulo/* 3898286441Srpaulo * END mvm/quota.c 3899286441Srpaulo */ 3900286441Srpaulo 3901286441Srpaulo/* 3902286441Srpaulo * ieee80211 routines 3903286441Srpaulo */ 3904286441Srpaulo 3905286441Srpaulo/* 3906286441Srpaulo * Change to AUTH state in 80211 state machine. Roughly matches what 3907286441Srpaulo * Linux does in bss_info_changed(). 3908286441Srpaulo */ 3909286441Srpaulostatic int 3910286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) 3911286441Srpaulo{ 3912286441Srpaulo struct ieee80211_node *ni; 3913286441Srpaulo struct iwm_node *in; 3914286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 3915286441Srpaulo uint32_t duration; 3916286441Srpaulo int error; 3917286441Srpaulo 3918286441Srpaulo /* 3919286441Srpaulo * XXX i have a feeling that the vap node is being 3920286441Srpaulo * freed from underneath us. Grr. 3921286441Srpaulo */ 3922286441Srpaulo ni = ieee80211_ref_node(vap->iv_bss); 3923293099Savos in = IWM_NODE(ni); 3924286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE, 3925286441Srpaulo "%s: called; vap=%p, bss ni=%p\n", 3926286441Srpaulo __func__, 3927286441Srpaulo vap, 3928286441Srpaulo ni); 3929286441Srpaulo 3930286441Srpaulo in->in_assoc = 0; 3931286441Srpaulo 3932303628Ssbruno error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON); 3933303628Ssbruno if (error != 0) 3934303628Ssbruno return error; 3935303628Ssbruno 3936286441Srpaulo error = iwm_allow_mcast(vap, sc); 3937286441Srpaulo if (error) { 3938286441Srpaulo device_printf(sc->sc_dev, 3939286441Srpaulo "%s: failed to set multicast\n", __func__); 3940286441Srpaulo goto out; 3941286441Srpaulo } 3942286441Srpaulo 3943286441Srpaulo /* 3944286441Srpaulo * This is where it deviates from what Linux does. 3945286441Srpaulo * 3946286441Srpaulo * Linux iwlwifi doesn't reset the nic each time, nor does it 3947286441Srpaulo * call ctxt_add() here. Instead, it adds it during vap creation, 3948303628Ssbruno * and always does a mac_ctx_changed(). 3949286441Srpaulo * 3950286441Srpaulo * The openbsd port doesn't attempt to do that - it reset things 3951286441Srpaulo * at odd states and does the add here. 3952286441Srpaulo * 3953286441Srpaulo * So, until the state handling is fixed (ie, we never reset 3954286441Srpaulo * the NIC except for a firmware failure, which should drag 3955286441Srpaulo * the NIC back to IDLE, re-setup and re-add all the mac/phy 3956286441Srpaulo * contexts that are required), let's do a dirty hack here. 3957286441Srpaulo */ 3958286441Srpaulo if (iv->is_uploaded) { 3959286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 3960286441Srpaulo device_printf(sc->sc_dev, 3961298582Sadrian "%s: failed to update MAC\n", __func__); 3962286441Srpaulo goto out; 3963286441Srpaulo } 3964298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 3965298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 3966298582Sadrian device_printf(sc->sc_dev, 3967298582Sadrian "%s: failed update phy ctxt\n", __func__); 3968298582Sadrian goto out; 3969298582Sadrian } 3970298582Sadrian in->in_phyctxt = &sc->sc_phyctxt[0]; 3971298582Sadrian 3972298582Sadrian if ((error = iwm_mvm_binding_update(sc, in)) != 0) { 3973298582Sadrian device_printf(sc->sc_dev, 3974298582Sadrian "%s: binding update cmd\n", __func__); 3975298582Sadrian goto out; 3976298582Sadrian } 3977298582Sadrian if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 3978298582Sadrian device_printf(sc->sc_dev, 3979298582Sadrian "%s: failed to update sta\n", __func__); 3980298582Sadrian goto out; 3981298582Sadrian } 3982286441Srpaulo } else { 3983286441Srpaulo if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) { 3984286441Srpaulo device_printf(sc->sc_dev, 3985286441Srpaulo "%s: failed to add MAC\n", __func__); 3986286441Srpaulo goto out; 3987286441Srpaulo } 3988298582Sadrian if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 3989298582Sadrian in->in_ni.ni_chan, 1, 1)) != 0) { 3990286441Srpaulo device_printf(sc->sc_dev, 3991298582Sadrian "%s: failed add phy ctxt!\n", __func__); 3992286441Srpaulo error = ETIMEDOUT; 3993286441Srpaulo goto out; 3994298582Sadrian } 3995298582Sadrian in->in_phyctxt = &sc->sc_phyctxt[0]; 3996298582Sadrian 3997298582Sadrian if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) { 3998286441Srpaulo device_printf(sc->sc_dev, 3999298582Sadrian "%s: binding add cmd\n", __func__); 4000286441Srpaulo goto out; 4001286441Srpaulo } 4002298582Sadrian if ((error = iwm_mvm_add_sta(sc, in)) != 0) { 4003298582Sadrian device_printf(sc->sc_dev, 4004298582Sadrian "%s: failed to add sta\n", __func__); 4005298582Sadrian goto out; 4006298582Sadrian } 4007286441Srpaulo } 4008298582Sadrian 4009298582Sadrian /* 4010298582Sadrian * Prevent the FW from wandering off channel during association 4011298582Sadrian * by "protecting" the session with a time event. 4012298582Sadrian */ 4013298582Sadrian /* XXX duration is in units of TU, not MS */ 4014298582Sadrian duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; 4015298582Sadrian iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */); 4016298582Sadrian DELAY(100); 4017298582Sadrian 4018286441Srpaulo error = 0; 4019286441Srpauloout: 4020286441Srpaulo ieee80211_free_node(ni); 4021286441Srpaulo return (error); 4022286441Srpaulo} 4023286441Srpaulo 4024286441Srpaulostatic int 4025286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc) 4026286441Srpaulo{ 4027293099Savos struct iwm_node *in = IWM_NODE(vap->iv_bss); 4028286441Srpaulo int error; 4029286441Srpaulo 4030286441Srpaulo if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 4031286441Srpaulo device_printf(sc->sc_dev, 4032286441Srpaulo "%s: failed to update STA\n", __func__); 4033286441Srpaulo return error; 4034286441Srpaulo } 4035286441Srpaulo 4036286441Srpaulo in->in_assoc = 1; 4037286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 4038286441Srpaulo device_printf(sc->sc_dev, 4039286441Srpaulo "%s: failed to update MAC\n", __func__); 4040286441Srpaulo return error; 4041286441Srpaulo } 4042286441Srpaulo 4043286441Srpaulo return 0; 4044286441Srpaulo} 4045286441Srpaulo 4046286441Srpaulostatic int 4047286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in) 4048286441Srpaulo{ 4049330154Seadler uint32_t tfd_msk; 4050330154Seadler 4051286441Srpaulo /* 4052286441Srpaulo * Ok, so *technically* the proper set of calls for going 4053286441Srpaulo * from RUN back to SCAN is: 4054286441Srpaulo * 4055286441Srpaulo * iwm_mvm_power_mac_disable(sc, in); 4056286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 4057286441Srpaulo * iwm_mvm_rm_sta(sc, in); 4058286441Srpaulo * iwm_mvm_update_quotas(sc, NULL); 4059286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 4060286441Srpaulo * iwm_mvm_binding_remove_vif(sc, in); 4061286441Srpaulo * iwm_mvm_mac_ctxt_remove(sc, in); 4062286441Srpaulo * 4063286441Srpaulo * However, that freezes the device not matter which permutations 4064286441Srpaulo * and modifications are attempted. Obviously, this driver is missing 4065286441Srpaulo * something since it works in the Linux driver, but figuring out what 4066286441Srpaulo * is missing is a little more complicated. Now, since we're going 4067286441Srpaulo * back to nothing anyway, we'll just do a complete device reset. 4068286441Srpaulo * Up your's, device! 4069286441Srpaulo */ 4070330154Seadler /* 4071330154Seadler * Just using 0xf for the queues mask is fine as long as we only 4072330154Seadler * get here from RUN state. 4073330154Seadler */ 4074330154Seadler tfd_msk = 0xf; 4075330154Seadler mbufq_drain(&sc->sc_snd); 4076330154Seadler iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC); 4077330154Seadler /* 4078330154Seadler * We seem to get away with just synchronously sending the 4079330154Seadler * IWM_TXPATH_FLUSH command. 4080330154Seadler */ 4081330154Seadler// iwm_trans_wait_tx_queue_empty(sc, tfd_msk); 4082286441Srpaulo iwm_stop_device(sc); 4083286441Srpaulo iwm_init_hw(sc); 4084286441Srpaulo if (in) 4085286441Srpaulo in->in_assoc = 0; 4086286441Srpaulo return 0; 4087286441Srpaulo 4088286441Srpaulo#if 0 4089286441Srpaulo int error; 4090286441Srpaulo 4091286441Srpaulo iwm_mvm_power_mac_disable(sc, in); 4092286441Srpaulo 4093286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 4094286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); 4095286441Srpaulo return error; 4096286441Srpaulo } 4097286441Srpaulo 4098286441Srpaulo if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { 4099286441Srpaulo device_printf(sc->sc_dev, "sta remove fail %d\n", error); 4100286441Srpaulo return error; 4101286441Srpaulo } 4102286441Srpaulo error = iwm_mvm_rm_sta(sc, in); 4103286441Srpaulo in->in_assoc = 0; 4104286441Srpaulo iwm_mvm_update_quotas(sc, NULL); 4105286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 4106286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); 4107286441Srpaulo return error; 4108286441Srpaulo } 4109286441Srpaulo iwm_mvm_binding_remove_vif(sc, in); 4110286441Srpaulo 4111286441Srpaulo iwm_mvm_mac_ctxt_remove(sc, in); 4112286441Srpaulo 4113286441Srpaulo return error; 4114286441Srpaulo#endif 4115286441Srpaulo} 4116286441Srpaulo 4117286441Srpaulostatic struct ieee80211_node * 4118286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 4119286441Srpaulo{ 4120286441Srpaulo return malloc(sizeof (struct iwm_node), M_80211_NODE, 4121286441Srpaulo M_NOWAIT | M_ZERO); 4122286441Srpaulo} 4123286441Srpaulo 4124286441Srpaulostatic void 4125286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in) 4126286441Srpaulo{ 4127286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 4128286441Srpaulo struct iwm_lq_cmd *lq = &in->in_lq; 4129286441Srpaulo int nrates = ni->ni_rates.rs_nrates; 4130286441Srpaulo int i, ridx, tab = 0; 4131330156Seadler// int txant = 0; 4132286441Srpaulo 4133286441Srpaulo if (nrates > nitems(lq->rs_table)) { 4134286441Srpaulo device_printf(sc->sc_dev, 4135286441Srpaulo "%s: node supports %d rates, driver handles " 4136286441Srpaulo "only %zu\n", __func__, nrates, nitems(lq->rs_table)); 4137286441Srpaulo return; 4138286441Srpaulo } 4139294248Sadrian if (nrates == 0) { 4140294248Sadrian device_printf(sc->sc_dev, 4141294248Sadrian "%s: node supports 0 rates, odd!\n", __func__); 4142294248Sadrian return; 4143294248Sadrian } 4144286441Srpaulo 4145286441Srpaulo /* 4146286441Srpaulo * XXX .. and most of iwm_node is not initialised explicitly; 4147286441Srpaulo * it's all just 0x0 passed to the firmware. 4148286441Srpaulo */ 4149286441Srpaulo 4150286441Srpaulo /* first figure out which rates we should support */ 4151286441Srpaulo /* XXX TODO: this isn't 11n aware /at all/ */ 4152286441Srpaulo memset(&in->in_ridx, -1, sizeof(in->in_ridx)); 4153286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4154286441Srpaulo "%s: nrates=%d\n", __func__, nrates); 4155286441Srpaulo 4156294248Sadrian /* 4157294248Sadrian * Loop over nrates and populate in_ridx from the highest 4158294248Sadrian * rate to the lowest rate. Remember, in_ridx[] has 4159294248Sadrian * IEEE80211_RATE_MAXSIZE entries! 4160294248Sadrian */ 4161294248Sadrian for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) { 4162294248Sadrian int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL; 4163294248Sadrian 4164286441Srpaulo /* Map 802.11 rate to HW rate index. */ 4165286441Srpaulo for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) 4166286441Srpaulo if (iwm_rates[ridx].rate == rate) 4167286441Srpaulo break; 4168286441Srpaulo if (ridx > IWM_RIDX_MAX) { 4169286441Srpaulo device_printf(sc->sc_dev, 4170286441Srpaulo "%s: WARNING: device rate for %d not found!\n", 4171286441Srpaulo __func__, rate); 4172286441Srpaulo } else { 4173286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4174286441Srpaulo "%s: rate: i: %d, rate=%d, ridx=%d\n", 4175286441Srpaulo __func__, 4176286441Srpaulo i, 4177286441Srpaulo rate, 4178286441Srpaulo ridx); 4179286441Srpaulo in->in_ridx[i] = ridx; 4180286441Srpaulo } 4181286441Srpaulo } 4182286441Srpaulo 4183286441Srpaulo /* then construct a lq_cmd based on those */ 4184286441Srpaulo memset(lq, 0, sizeof(*lq)); 4185286441Srpaulo lq->sta_id = IWM_STATION_ID; 4186286441Srpaulo 4187303628Ssbruno /* For HT, always enable RTS/CTS to avoid excessive retries. */ 4188303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) 4189303628Ssbruno lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; 4190303628Ssbruno 4191286441Srpaulo /* 4192286441Srpaulo * are these used? (we don't do SISO or MIMO) 4193286441Srpaulo * need to set them to non-zero, though, or we get an error. 4194286441Srpaulo */ 4195286441Srpaulo lq->single_stream_ant_msk = 1; 4196286441Srpaulo lq->dual_stream_ant_msk = 1; 4197286441Srpaulo 4198286441Srpaulo /* 4199286441Srpaulo * Build the actual rate selection table. 4200286441Srpaulo * The lowest bits are the rates. Additionally, 4201286441Srpaulo * CCK needs bit 9 to be set. The rest of the bits 4202286441Srpaulo * we add to the table select the tx antenna 4203286441Srpaulo * Note that we add the rates in the highest rate first 4204286441Srpaulo * (opposite of ni_rates). 4205286441Srpaulo */ 4206286441Srpaulo /* 4207286441Srpaulo * XXX TODO: this should be looping over the min of nrates 4208286441Srpaulo * and LQ_MAX_RETRY_NUM. Sigh. 4209286441Srpaulo */ 4210286441Srpaulo for (i = 0; i < nrates; i++) { 4211286441Srpaulo int nextant; 4212286441Srpaulo 4213330156Seadler#if 0 4214286441Srpaulo if (txant == 0) 4215303628Ssbruno txant = iwm_fw_valid_tx_ant(sc); 4216286441Srpaulo nextant = 1<<(ffs(txant)-1); 4217286441Srpaulo txant &= ~nextant; 4218330156Seadler#else 4219330156Seadler nextant = iwm_fw_valid_tx_ant(sc); 4220330156Seadler#endif 4221286441Srpaulo /* 4222286441Srpaulo * Map the rate id into a rate index into 4223286441Srpaulo * our hardware table containing the 4224286441Srpaulo * configuration to use for this rate. 4225286441Srpaulo */ 4226294248Sadrian ridx = in->in_ridx[i]; 4227286441Srpaulo tab = iwm_rates[ridx].plcp; 4228286441Srpaulo tab |= nextant << IWM_RATE_MCS_ANT_POS; 4229286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 4230286441Srpaulo tab |= IWM_RATE_MCS_CCK_MSK; 4231286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 4232286441Srpaulo "station rate i=%d, rate=%d, hw=%x\n", 4233286441Srpaulo i, iwm_rates[ridx].rate, tab); 4234286441Srpaulo lq->rs_table[i] = htole32(tab); 4235286441Srpaulo } 4236286441Srpaulo /* then fill the rest with the lowest possible rate */ 4237286441Srpaulo for (i = nrates; i < nitems(lq->rs_table); i++) { 4238286441Srpaulo KASSERT(tab != 0, ("invalid tab")); 4239286441Srpaulo lq->rs_table[i] = htole32(tab); 4240286441Srpaulo } 4241286441Srpaulo} 4242286441Srpaulo 4243286441Srpaulostatic int 4244286441Srpauloiwm_media_change(struct ifnet *ifp) 4245286441Srpaulo{ 4246287197Sglebius struct ieee80211vap *vap = ifp->if_softc; 4247287197Sglebius struct ieee80211com *ic = vap->iv_ic; 4248287197Sglebius struct iwm_softc *sc = ic->ic_softc; 4249286441Srpaulo int error; 4250286441Srpaulo 4251286441Srpaulo error = ieee80211_media_change(ifp); 4252286441Srpaulo if (error != ENETRESET) 4253286441Srpaulo return error; 4254286441Srpaulo 4255287197Sglebius IWM_LOCK(sc); 4256287197Sglebius if (ic->ic_nrunning > 0) { 4257287197Sglebius iwm_stop(sc); 4258286441Srpaulo iwm_init(sc); 4259286441Srpaulo } 4260287197Sglebius IWM_UNLOCK(sc); 4261286441Srpaulo return error; 4262286441Srpaulo} 4263286441Srpaulo 4264286441Srpaulo 4265286441Srpaulostatic int 4266286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 4267286441Srpaulo{ 4268286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 4269286441Srpaulo struct ieee80211com *ic = vap->iv_ic; 4270286865Sadrian struct iwm_softc *sc = ic->ic_softc; 4271286441Srpaulo struct iwm_node *in; 4272286441Srpaulo int error; 4273286441Srpaulo 4274286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4275286441Srpaulo "switching state %s -> %s\n", 4276286441Srpaulo ieee80211_state_name[vap->iv_state], 4277286441Srpaulo ieee80211_state_name[nstate]); 4278286441Srpaulo IEEE80211_UNLOCK(ic); 4279286441Srpaulo IWM_LOCK(sc); 4280301187Sadrian 4281301187Sadrian if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state) 4282301187Sadrian iwm_led_blink_stop(sc); 4283301187Sadrian 4284286441Srpaulo /* disable beacon filtering if we're hopping out of RUN */ 4285286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { 4286286441Srpaulo iwm_mvm_disable_beacon_filter(sc); 4287286441Srpaulo 4288293099Savos if (((in = IWM_NODE(vap->iv_bss)) != NULL)) 4289286441Srpaulo in->in_assoc = 0; 4290286441Srpaulo 4291330154Seadler if (nstate == IEEE80211_S_INIT) { 4292330154Seadler IWM_UNLOCK(sc); 4293330154Seadler IEEE80211_LOCK(ic); 4294330154Seadler error = ivp->iv_newstate(vap, nstate, arg); 4295330154Seadler IEEE80211_UNLOCK(ic); 4296330154Seadler IWM_LOCK(sc); 4297330154Seadler iwm_release(sc, NULL); 4298330154Seadler IWM_UNLOCK(sc); 4299330154Seadler IEEE80211_LOCK(ic); 4300330154Seadler return error; 4301330154Seadler } 4302286441Srpaulo 4303286441Srpaulo /* 4304286441Srpaulo * It's impossible to directly go RUN->SCAN. If we iwm_release() 4305286441Srpaulo * above then the card will be completely reinitialized, 4306286441Srpaulo * so the driver must do everything necessary to bring the card 4307286441Srpaulo * from INIT to SCAN. 4308286441Srpaulo * 4309286441Srpaulo * Additionally, upon receiving deauth frame from AP, 4310286441Srpaulo * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH 4311286441Srpaulo * state. This will also fail with this driver, so bring the FSM 4312286441Srpaulo * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. 4313286441Srpaulo * 4314286441Srpaulo * XXX TODO: fix this for FreeBSD! 4315286441Srpaulo */ 4316286441Srpaulo if (nstate == IEEE80211_S_SCAN || 4317286441Srpaulo nstate == IEEE80211_S_AUTH || 4318286441Srpaulo nstate == IEEE80211_S_ASSOC) { 4319286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4320286441Srpaulo "Force transition to INIT; MGT=%d\n", arg); 4321286441Srpaulo IWM_UNLOCK(sc); 4322286441Srpaulo IEEE80211_LOCK(ic); 4323303628Ssbruno /* Always pass arg as -1 since we can't Tx right now. */ 4324303628Ssbruno /* 4325303628Ssbruno * XXX arg is just ignored anyway when transitioning 4326303628Ssbruno * to IEEE80211_S_INIT. 4327303628Ssbruno */ 4328303628Ssbruno vap->iv_newstate(vap, IEEE80211_S_INIT, -1); 4329286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 4330286441Srpaulo "Going INIT->SCAN\n"); 4331286441Srpaulo nstate = IEEE80211_S_SCAN; 4332286441Srpaulo IEEE80211_UNLOCK(ic); 4333286441Srpaulo IWM_LOCK(sc); 4334286441Srpaulo } 4335286441Srpaulo } 4336286441Srpaulo 4337286441Srpaulo switch (nstate) { 4338286441Srpaulo case IEEE80211_S_INIT: 4339286441Srpaulo break; 4340286441Srpaulo 4341286441Srpaulo case IEEE80211_S_AUTH: 4342286441Srpaulo if ((error = iwm_auth(vap, sc)) != 0) { 4343286441Srpaulo device_printf(sc->sc_dev, 4344286441Srpaulo "%s: could not move to auth state: %d\n", 4345286441Srpaulo __func__, error); 4346286441Srpaulo break; 4347286441Srpaulo } 4348286441Srpaulo break; 4349286441Srpaulo 4350286441Srpaulo case IEEE80211_S_ASSOC: 4351286441Srpaulo if ((error = iwm_assoc(vap, sc)) != 0) { 4352286441Srpaulo device_printf(sc->sc_dev, 4353286441Srpaulo "%s: failed to associate: %d\n", __func__, 4354286441Srpaulo error); 4355286441Srpaulo break; 4356286441Srpaulo } 4357286441Srpaulo break; 4358286441Srpaulo 4359286441Srpaulo case IEEE80211_S_RUN: 4360286441Srpaulo { 4361286441Srpaulo struct iwm_host_cmd cmd = { 4362286441Srpaulo .id = IWM_LQ_CMD, 4363286441Srpaulo .len = { sizeof(in->in_lq), }, 4364286441Srpaulo .flags = IWM_CMD_SYNC, 4365286441Srpaulo }; 4366286441Srpaulo 4367286441Srpaulo /* Update the association state, now we have it all */ 4368286441Srpaulo /* (eg associd comes in at this point */ 4369286441Srpaulo error = iwm_assoc(vap, sc); 4370286441Srpaulo if (error != 0) { 4371286441Srpaulo device_printf(sc->sc_dev, 4372286441Srpaulo "%s: failed to update association state: %d\n", 4373286441Srpaulo __func__, 4374286441Srpaulo error); 4375286441Srpaulo break; 4376286441Srpaulo } 4377286441Srpaulo 4378293099Savos in = IWM_NODE(vap->iv_bss); 4379286441Srpaulo iwm_mvm_power_mac_update_mode(sc, in); 4380286441Srpaulo iwm_mvm_enable_beacon_filter(sc, in); 4381286441Srpaulo iwm_mvm_update_quotas(sc, in); 4382286441Srpaulo iwm_setrates(sc, in); 4383286441Srpaulo 4384286441Srpaulo cmd.data[0] = &in->in_lq; 4385286441Srpaulo if ((error = iwm_send_cmd(sc, &cmd)) != 0) { 4386286441Srpaulo device_printf(sc->sc_dev, 4387286441Srpaulo "%s: IWM_LQ_CMD failed\n", __func__); 4388286441Srpaulo } 4389286441Srpaulo 4390303628Ssbruno iwm_mvm_led_enable(sc); 4391286441Srpaulo break; 4392286441Srpaulo } 4393286441Srpaulo 4394286441Srpaulo default: 4395286441Srpaulo break; 4396286441Srpaulo } 4397286441Srpaulo IWM_UNLOCK(sc); 4398286441Srpaulo IEEE80211_LOCK(ic); 4399286441Srpaulo 4400286441Srpaulo return (ivp->iv_newstate(vap, nstate, arg)); 4401286441Srpaulo} 4402286441Srpaulo 4403286441Srpaulovoid 4404286441Srpauloiwm_endscan_cb(void *arg, int pending) 4405286441Srpaulo{ 4406286441Srpaulo struct iwm_softc *sc = arg; 4407287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4408286441Srpaulo 4409286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE, 4410286441Srpaulo "%s: scan ended\n", 4411286441Srpaulo __func__); 4412286441Srpaulo 4413303628Ssbruno ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); 4414303628Ssbruno} 4415303628Ssbruno 4416303628Ssbruno/* 4417303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4418303628Ssbruno * in default configuration 4419303628Ssbruno */ 4420303628Ssbrunostatic const uint32_t 4421303628Ssbrunoiwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4422303628Ssbruno { 4423303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF), 4424303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF) 4425303628Ssbruno }, 4426303628Ssbruno { 4427303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF), 4428303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF) 4429303628Ssbruno }, 4430303628Ssbruno { 4431303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER_DEF), 4432303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER_DEF) 4433303628Ssbruno }, 4434303628Ssbruno { 4435303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER_DEF), 4436303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER_DEF) 4437303628Ssbruno }, 4438303628Ssbruno { 4439303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER_DEF), 4440303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF) 4441303628Ssbruno }, 4442303628Ssbruno}; 4443303628Ssbruno 4444303628Ssbruno/* 4445303628Ssbruno * Aging and idle timeouts for the different possible scenarios 4446303628Ssbruno * in single BSS MAC configuration. 4447303628Ssbruno */ 4448303628Ssbrunostatic const uint32_t 4449303628Ssbrunoiwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { 4450303628Ssbruno { 4451303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER), 4452303628Ssbruno htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER) 4453303628Ssbruno }, 4454303628Ssbruno { 4455303628Ssbruno htole32(IWM_SF_AGG_UNICAST_AGING_TIMER), 4456303628Ssbruno htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER) 4457303628Ssbruno }, 4458303628Ssbruno { 4459303628Ssbruno htole32(IWM_SF_MCAST_AGING_TIMER), 4460303628Ssbruno htole32(IWM_SF_MCAST_IDLE_TIMER) 4461303628Ssbruno }, 4462303628Ssbruno { 4463303628Ssbruno htole32(IWM_SF_BA_AGING_TIMER), 4464303628Ssbruno htole32(IWM_SF_BA_IDLE_TIMER) 4465303628Ssbruno }, 4466303628Ssbruno { 4467303628Ssbruno htole32(IWM_SF_TX_RE_AGING_TIMER), 4468303628Ssbruno htole32(IWM_SF_TX_RE_IDLE_TIMER) 4469303628Ssbruno }, 4470303628Ssbruno}; 4471303628Ssbruno 4472303628Ssbrunostatic void 4473303628Ssbrunoiwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd, 4474303628Ssbruno struct ieee80211_node *ni) 4475303628Ssbruno{ 4476303628Ssbruno int i, j, watermark; 4477303628Ssbruno 4478303628Ssbruno sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN); 4479303628Ssbruno 4480303628Ssbruno /* 4481303628Ssbruno * If we are in association flow - check antenna configuration 4482303628Ssbruno * capabilities of the AP station, and choose the watermark accordingly. 4483303628Ssbruno */ 4484303628Ssbruno if (ni) { 4485303628Ssbruno if (ni->ni_flags & IEEE80211_NODE_HT) { 4486303628Ssbruno#ifdef notyet 4487303628Ssbruno if (ni->ni_rxmcs[2] != 0) 4488303628Ssbruno watermark = IWM_SF_W_MARK_MIMO3; 4489303628Ssbruno else if (ni->ni_rxmcs[1] != 0) 4490303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4491303628Ssbruno else 4492303628Ssbruno#endif 4493303628Ssbruno watermark = IWM_SF_W_MARK_SISO; 4494303628Ssbruno } else { 4495303628Ssbruno watermark = IWM_SF_W_MARK_LEGACY; 4496286441Srpaulo } 4497303628Ssbruno /* default watermark value for unassociated mode. */ 4498286441Srpaulo } else { 4499303628Ssbruno watermark = IWM_SF_W_MARK_MIMO2; 4500286441Srpaulo } 4501303628Ssbruno sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark); 4502286441Srpaulo 4503303628Ssbruno for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) { 4504303628Ssbruno for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) { 4505303628Ssbruno sf_cmd->long_delay_timeouts[i][j] = 4506303628Ssbruno htole32(IWM_SF_LONG_DELAY_AGING_TIMER); 4507303628Ssbruno } 4508286441Srpaulo } 4509303628Ssbruno 4510303628Ssbruno if (ni) { 4511303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout, 4512303628Ssbruno sizeof(iwm_sf_full_timeout)); 4513303628Ssbruno } else { 4514303628Ssbruno memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def, 4515303628Ssbruno sizeof(iwm_sf_full_timeout_def)); 4516303628Ssbruno } 4517286441Srpaulo} 4518286441Srpaulo 4519286441Srpaulostatic int 4520303628Ssbrunoiwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) 4521303628Ssbruno{ 4522303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 4523303628Ssbruno struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4524303628Ssbruno struct iwm_sf_cfg_cmd sf_cmd = { 4525303628Ssbruno .state = htole32(IWM_SF_FULL_ON), 4526303628Ssbruno }; 4527303628Ssbruno int ret = 0; 4528303628Ssbruno 4529330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 4530303628Ssbruno sf_cmd.state |= htole32(IWM_SF_CFG_DUMMY_NOTIF_OFF); 4531303628Ssbruno 4532303628Ssbruno switch (new_state) { 4533303628Ssbruno case IWM_SF_UNINIT: 4534303628Ssbruno case IWM_SF_INIT_OFF: 4535303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL); 4536303628Ssbruno break; 4537303628Ssbruno case IWM_SF_FULL_ON: 4538303628Ssbruno iwm_mvm_fill_sf_command(sc, &sf_cmd, vap->iv_bss); 4539303628Ssbruno break; 4540303628Ssbruno default: 4541303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_PWRSAVE, 4542303628Ssbruno "Invalid state: %d. not sending Smart Fifo cmd\n", 4543303628Ssbruno new_state); 4544303628Ssbruno return EINVAL; 4545303628Ssbruno } 4546303628Ssbruno 4547303628Ssbruno ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC, 4548303628Ssbruno sizeof(sf_cmd), &sf_cmd); 4549303628Ssbruno return ret; 4550303628Ssbruno} 4551303628Ssbruno 4552303628Ssbrunostatic int 4553303628Ssbrunoiwm_send_bt_init_conf(struct iwm_softc *sc) 4554303628Ssbruno{ 4555303628Ssbruno struct iwm_bt_coex_cmd bt_cmd; 4556303628Ssbruno 4557303628Ssbruno bt_cmd.mode = htole32(IWM_BT_COEX_WIFI); 4558303628Ssbruno bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET); 4559303628Ssbruno 4560303628Ssbruno return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd), 4561303628Ssbruno &bt_cmd); 4562303628Ssbruno} 4563303628Ssbruno 4564303628Ssbrunostatic int 4565303628Ssbrunoiwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) 4566303628Ssbruno{ 4567303628Ssbruno struct iwm_mcc_update_cmd mcc_cmd; 4568303628Ssbruno struct iwm_host_cmd hcmd = { 4569303628Ssbruno .id = IWM_MCC_UPDATE_CMD, 4570303628Ssbruno .flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB), 4571303628Ssbruno .data = { &mcc_cmd }, 4572303628Ssbruno }; 4573303628Ssbruno int ret; 4574303628Ssbruno#ifdef IWM_DEBUG 4575303628Ssbruno struct iwm_rx_packet *pkt; 4576303628Ssbruno struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL; 4577303628Ssbruno struct iwm_mcc_update_resp *mcc_resp; 4578303628Ssbruno int n_channels; 4579303628Ssbruno uint16_t mcc; 4580303628Ssbruno#endif 4581303628Ssbruno int resp_v2 = isset(sc->sc_enabled_capa, 4582303628Ssbruno IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); 4583303628Ssbruno 4584303628Ssbruno memset(&mcc_cmd, 0, sizeof(mcc_cmd)); 4585303628Ssbruno mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]); 4586303628Ssbruno if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || 4587303628Ssbruno isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC)) 4588303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT; 4589303628Ssbruno else 4590303628Ssbruno mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW; 4591303628Ssbruno 4592303628Ssbruno if (resp_v2) 4593303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd); 4594303628Ssbruno else 4595303628Ssbruno hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1); 4596303628Ssbruno 4597303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_NODE, 4598303628Ssbruno "send MCC update to FW with '%c%c' src = %d\n", 4599303628Ssbruno alpha2[0], alpha2[1], mcc_cmd.source_id); 4600303628Ssbruno 4601303628Ssbruno ret = iwm_send_cmd(sc, &hcmd); 4602303628Ssbruno if (ret) 4603303628Ssbruno return ret; 4604303628Ssbruno 4605303628Ssbruno#ifdef IWM_DEBUG 4606303628Ssbruno pkt = hcmd.resp_pkt; 4607303628Ssbruno 4608303628Ssbruno /* Extract MCC response */ 4609303628Ssbruno if (resp_v2) { 4610303628Ssbruno mcc_resp = (void *)pkt->data; 4611303628Ssbruno mcc = mcc_resp->mcc; 4612303628Ssbruno n_channels = le32toh(mcc_resp->n_channels); 4613303628Ssbruno } else { 4614303628Ssbruno mcc_resp_v1 = (void *)pkt->data; 4615303628Ssbruno mcc = mcc_resp_v1->mcc; 4616303628Ssbruno n_channels = le32toh(mcc_resp_v1->n_channels); 4617303628Ssbruno } 4618303628Ssbruno 4619303628Ssbruno /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ 4620303628Ssbruno if (mcc == 0) 4621303628Ssbruno mcc = 0x3030; /* "00" - world */ 4622303628Ssbruno 4623303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_NODE, 4624303628Ssbruno "regulatory domain '%c%c' (%d channels available)\n", 4625303628Ssbruno mcc >> 8, mcc & 0xff, n_channels); 4626303628Ssbruno#endif 4627303628Ssbruno iwm_free_resp(sc, &hcmd); 4628303628Ssbruno 4629303628Ssbruno return 0; 4630303628Ssbruno} 4631303628Ssbruno 4632303628Ssbrunostatic void 4633303628Ssbrunoiwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff) 4634303628Ssbruno{ 4635303628Ssbruno struct iwm_host_cmd cmd = { 4636303628Ssbruno .id = IWM_REPLY_THERMAL_MNG_BACKOFF, 4637303628Ssbruno .len = { sizeof(uint32_t), }, 4638303628Ssbruno .data = { &backoff, }, 4639303628Ssbruno }; 4640303628Ssbruno 4641303628Ssbruno if (iwm_send_cmd(sc, &cmd) != 0) { 4642303628Ssbruno device_printf(sc->sc_dev, 4643303628Ssbruno "failed to change thermal tx backoff\n"); 4644303628Ssbruno } 4645303628Ssbruno} 4646303628Ssbruno 4647303628Ssbrunostatic int 4648286441Srpauloiwm_init_hw(struct iwm_softc *sc) 4649286441Srpaulo{ 4650287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4651303628Ssbruno int error, i, ac; 4652286441Srpaulo 4653303628Ssbruno if ((error = iwm_start_hw(sc)) != 0) { 4654303628Ssbruno printf("iwm_start_hw: failed %d\n", error); 4655286441Srpaulo return error; 4656303628Ssbruno } 4657286441Srpaulo 4658286441Srpaulo if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) { 4659303628Ssbruno printf("iwm_run_init_mvm_ucode: failed %d\n", error); 4660286441Srpaulo return error; 4661286441Srpaulo } 4662286441Srpaulo 4663286441Srpaulo /* 4664286441Srpaulo * should stop and start HW since that INIT 4665286441Srpaulo * image just loaded 4666286441Srpaulo */ 4667286441Srpaulo iwm_stop_device(sc); 4668286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 4669286441Srpaulo device_printf(sc->sc_dev, "could not initialize hardware\n"); 4670286441Srpaulo return error; 4671286441Srpaulo } 4672286441Srpaulo 4673286441Srpaulo /* omstart, this time with the regular firmware */ 4674286441Srpaulo error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR); 4675286441Srpaulo if (error) { 4676286441Srpaulo device_printf(sc->sc_dev, "could not load firmware\n"); 4677286441Srpaulo goto error; 4678286441Srpaulo } 4679286441Srpaulo 4680303628Ssbruno if ((error = iwm_send_bt_init_conf(sc)) != 0) { 4681303628Ssbruno device_printf(sc->sc_dev, "bt init conf failed\n"); 4682286441Srpaulo goto error; 4683303628Ssbruno } 4684286441Srpaulo 4685303628Ssbruno if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { 4686303628Ssbruno device_printf(sc->sc_dev, "antenna config failed\n"); 4687303628Ssbruno goto error; 4688303628Ssbruno } 4689303628Ssbruno 4690330163Seadler /* Send phy db control command and then phy db calibration */ 4691330163Seadler if ((error = iwm_send_phy_db_data(sc->sc_phy_db)) != 0) 4692286441Srpaulo goto error; 4693286441Srpaulo 4694303628Ssbruno if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) { 4695303628Ssbruno device_printf(sc->sc_dev, "phy_cfg_cmd failed\n"); 4696286441Srpaulo goto error; 4697303628Ssbruno } 4698286441Srpaulo 4699286441Srpaulo /* Add auxiliary station for scanning */ 4700303628Ssbruno if ((error = iwm_mvm_add_aux_sta(sc)) != 0) { 4701303628Ssbruno device_printf(sc->sc_dev, "add_aux_sta failed\n"); 4702286441Srpaulo goto error; 4703303628Ssbruno } 4704286441Srpaulo 4705286441Srpaulo for (i = 0; i < IWM_NUM_PHY_CTX; i++) { 4706286441Srpaulo /* 4707286441Srpaulo * The channel used here isn't relevant as it's 4708286441Srpaulo * going to be overwritten in the other flows. 4709286441Srpaulo * For now use the first channel we have. 4710286441Srpaulo */ 4711286441Srpaulo if ((error = iwm_mvm_phy_ctxt_add(sc, 4712286441Srpaulo &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0) 4713286441Srpaulo goto error; 4714286441Srpaulo } 4715286441Srpaulo 4716303628Ssbruno /* Initialize tx backoffs to the minimum. */ 4717330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) 4718303628Ssbruno iwm_mvm_tt_tx_backoff(sc, 0); 4719303628Ssbruno 4720286441Srpaulo error = iwm_mvm_power_update_device(sc); 4721286441Srpaulo if (error) 4722286441Srpaulo goto error; 4723286441Srpaulo 4724303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) { 4725303628Ssbruno if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) 4726303628Ssbruno goto error; 4727286441Srpaulo } 4728286441Srpaulo 4729303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { 4730303628Ssbruno if ((error = iwm_mvm_config_umac_scan(sc)) != 0) 4731303628Ssbruno goto error; 4732303628Ssbruno } 4733303628Ssbruno 4734303628Ssbruno /* Enable Tx queues. */ 4735303628Ssbruno for (ac = 0; ac < WME_NUM_AC; ac++) { 4736303628Ssbruno error = iwm_enable_txq(sc, IWM_STATION_ID, ac, 4737303628Ssbruno iwm_mvm_ac_to_tx_fifo[ac]); 4738303628Ssbruno if (error) 4739303628Ssbruno goto error; 4740303628Ssbruno } 4741303628Ssbruno 4742303628Ssbruno if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) { 4743303628Ssbruno device_printf(sc->sc_dev, "failed to disable beacon filter\n"); 4744303628Ssbruno goto error; 4745303628Ssbruno } 4746303628Ssbruno 4747286441Srpaulo return 0; 4748286441Srpaulo 4749286441Srpaulo error: 4750286441Srpaulo iwm_stop_device(sc); 4751286441Srpaulo return error; 4752286441Srpaulo} 4753286441Srpaulo 4754286441Srpaulo/* Allow multicast from our BSSID. */ 4755286441Srpaulostatic int 4756286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc) 4757286441Srpaulo{ 4758286441Srpaulo struct ieee80211_node *ni = vap->iv_bss; 4759286441Srpaulo struct iwm_mcast_filter_cmd *cmd; 4760286441Srpaulo size_t size; 4761286441Srpaulo int error; 4762286441Srpaulo 4763286441Srpaulo size = roundup(sizeof(*cmd), 4); 4764286441Srpaulo cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); 4765286441Srpaulo if (cmd == NULL) 4766286441Srpaulo return ENOMEM; 4767286441Srpaulo cmd->filter_own = 1; 4768286441Srpaulo cmd->port_id = 0; 4769286441Srpaulo cmd->count = 0; 4770286441Srpaulo cmd->pass_all = 1; 4771286441Srpaulo IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); 4772286441Srpaulo 4773286441Srpaulo error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 4774286441Srpaulo IWM_CMD_SYNC, size, cmd); 4775286441Srpaulo free(cmd, M_DEVBUF); 4776286441Srpaulo 4777286441Srpaulo return (error); 4778286441Srpaulo} 4779286441Srpaulo 4780303628Ssbruno/* 4781303628Ssbruno * ifnet interfaces 4782303628Ssbruno */ 4783303628Ssbruno 4784286441Srpaulostatic void 4785287197Sglebiusiwm_init(struct iwm_softc *sc) 4786286441Srpaulo{ 4787286441Srpaulo int error; 4788286441Srpaulo 4789286441Srpaulo if (sc->sc_flags & IWM_FLAG_HW_INITED) { 4790286441Srpaulo return; 4791286441Srpaulo } 4792286441Srpaulo sc->sc_generation++; 4793286441Srpaulo sc->sc_flags &= ~IWM_FLAG_STOPPED; 4794286441Srpaulo 4795286441Srpaulo if ((error = iwm_init_hw(sc)) != 0) { 4796303628Ssbruno printf("iwm_init_hw failed %d\n", error); 4797287197Sglebius iwm_stop(sc); 4798286441Srpaulo return; 4799286441Srpaulo } 4800286441Srpaulo 4801286441Srpaulo /* 4802303628Ssbruno * Ok, firmware loaded and we are jogging 4803286441Srpaulo */ 4804286441Srpaulo sc->sc_flags |= IWM_FLAG_HW_INITED; 4805286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 4806286441Srpaulo} 4807286441Srpaulo 4808287197Sglebiusstatic int 4809287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m) 4810286441Srpaulo{ 4811287197Sglebius struct iwm_softc *sc; 4812287197Sglebius int error; 4813286441Srpaulo 4814287197Sglebius sc = ic->ic_softc; 4815287197Sglebius 4816286441Srpaulo IWM_LOCK(sc); 4817287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 4818287197Sglebius IWM_UNLOCK(sc); 4819287197Sglebius return (ENXIO); 4820287197Sglebius } 4821287197Sglebius error = mbufq_enqueue(&sc->sc_snd, m); 4822287197Sglebius if (error) { 4823287197Sglebius IWM_UNLOCK(sc); 4824287197Sglebius return (error); 4825287197Sglebius } 4826287197Sglebius iwm_start(sc); 4827286441Srpaulo IWM_UNLOCK(sc); 4828287197Sglebius return (0); 4829286441Srpaulo} 4830286441Srpaulo 4831287197Sglebius/* 4832287197Sglebius * Dequeue packets from sendq and call send. 4833287197Sglebius */ 4834286441Srpaulostatic void 4835287197Sglebiusiwm_start(struct iwm_softc *sc) 4836286441Srpaulo{ 4837286441Srpaulo struct ieee80211_node *ni; 4838286441Srpaulo struct mbuf *m; 4839286441Srpaulo int ac = 0; 4840286441Srpaulo 4841286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__); 4842287197Sglebius while (sc->qfullmsk == 0 && 4843287197Sglebius (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 4844286441Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 4845286441Srpaulo if (iwm_tx(sc, m, ni, ac) != 0) { 4846287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 4847287197Sglebius IFCOUNTER_OERRORS, 1); 4848286441Srpaulo ieee80211_free_node(ni); 4849286441Srpaulo continue; 4850286441Srpaulo } 4851287197Sglebius sc->sc_tx_timer = 15; 4852286441Srpaulo } 4853286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); 4854286441Srpaulo} 4855286441Srpaulo 4856286441Srpaulostatic void 4857287197Sglebiusiwm_stop(struct iwm_softc *sc) 4858286441Srpaulo{ 4859286441Srpaulo 4860286441Srpaulo sc->sc_flags &= ~IWM_FLAG_HW_INITED; 4861286441Srpaulo sc->sc_flags |= IWM_FLAG_STOPPED; 4862286441Srpaulo sc->sc_generation++; 4863301187Sadrian iwm_led_blink_stop(sc); 4864286441Srpaulo sc->sc_tx_timer = 0; 4865286441Srpaulo iwm_stop_device(sc); 4866286441Srpaulo} 4867286441Srpaulo 4868286441Srpaulostatic void 4869286441Srpauloiwm_watchdog(void *arg) 4870286441Srpaulo{ 4871286441Srpaulo struct iwm_softc *sc = arg; 4872300242Savos struct ieee80211com *ic = &sc->sc_ic; 4873286441Srpaulo 4874286441Srpaulo if (sc->sc_tx_timer > 0) { 4875286441Srpaulo if (--sc->sc_tx_timer == 0) { 4876286441Srpaulo device_printf(sc->sc_dev, "device timeout\n"); 4877286441Srpaulo#ifdef IWM_DEBUG 4878286441Srpaulo iwm_nic_error(sc); 4879286441Srpaulo#endif 4880300242Savos ieee80211_restart_all(ic); 4881303628Ssbruno counter_u64_add(sc->sc_ic.ic_oerrors, 1); 4882286441Srpaulo return; 4883286441Srpaulo } 4884286441Srpaulo } 4885286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 4886286441Srpaulo} 4887286441Srpaulo 4888287197Sglebiusstatic void 4889287197Sglebiusiwm_parent(struct ieee80211com *ic) 4890286441Srpaulo{ 4891287197Sglebius struct iwm_softc *sc = ic->ic_softc; 4892287197Sglebius int startall = 0; 4893286441Srpaulo 4894287197Sglebius IWM_LOCK(sc); 4895287197Sglebius if (ic->ic_nrunning > 0) { 4896287197Sglebius if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) { 4897287197Sglebius iwm_init(sc); 4898287197Sglebius startall = 1; 4899286441Srpaulo } 4900287197Sglebius } else if (sc->sc_flags & IWM_FLAG_HW_INITED) 4901287197Sglebius iwm_stop(sc); 4902287197Sglebius IWM_UNLOCK(sc); 4903287197Sglebius if (startall) 4904287197Sglebius ieee80211_start_all(ic); 4905286441Srpaulo} 4906286441Srpaulo 4907286441Srpaulo/* 4908286441Srpaulo * The interrupt side of things 4909286441Srpaulo */ 4910286441Srpaulo 4911286441Srpaulo/* 4912286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c 4913286441Srpaulo */ 4914286441Srpaulo 4915286441Srpaulo/* 4916286441Srpaulo * Note: This structure is read from the device with IO accesses, 4917286441Srpaulo * and the reading already does the endian conversion. As it is 4918286441Srpaulo * read with uint32_t-sized accesses, any members with a different size 4919286441Srpaulo * need to be ordered correctly though! 4920286441Srpaulo */ 4921286441Srpaulostruct iwm_error_event_table { 4922286441Srpaulo uint32_t valid; /* (nonzero) valid, (0) log is empty */ 4923286441Srpaulo uint32_t error_id; /* type of error */ 4924303628Ssbruno uint32_t trm_hw_status0; /* TRM HW status */ 4925303628Ssbruno uint32_t trm_hw_status1; /* TRM HW status */ 4926286441Srpaulo uint32_t blink2; /* branch link */ 4927286441Srpaulo uint32_t ilink1; /* interrupt link */ 4928286441Srpaulo uint32_t ilink2; /* interrupt link */ 4929286441Srpaulo uint32_t data1; /* error-specific data */ 4930286441Srpaulo uint32_t data2; /* error-specific data */ 4931286441Srpaulo uint32_t data3; /* error-specific data */ 4932286441Srpaulo uint32_t bcon_time; /* beacon timer */ 4933286441Srpaulo uint32_t tsf_low; /* network timestamp function timer */ 4934286441Srpaulo uint32_t tsf_hi; /* network timestamp function timer */ 4935286441Srpaulo uint32_t gp1; /* GP1 timer register */ 4936286441Srpaulo uint32_t gp2; /* GP2 timer register */ 4937303628Ssbruno uint32_t fw_rev_type; /* firmware revision type */ 4938303628Ssbruno uint32_t major; /* uCode version major */ 4939303628Ssbruno uint32_t minor; /* uCode version minor */ 4940286441Srpaulo uint32_t hw_ver; /* HW Silicon version */ 4941286441Srpaulo uint32_t brd_ver; /* HW board version */ 4942286441Srpaulo uint32_t log_pc; /* log program counter */ 4943286441Srpaulo uint32_t frame_ptr; /* frame pointer */ 4944286441Srpaulo uint32_t stack_ptr; /* stack pointer */ 4945286441Srpaulo uint32_t hcmd; /* last host command header */ 4946286441Srpaulo uint32_t isr0; /* isr status register LMPM_NIC_ISR0: 4947286441Srpaulo * rxtx_flag */ 4948286441Srpaulo uint32_t isr1; /* isr status register LMPM_NIC_ISR1: 4949286441Srpaulo * host_flag */ 4950286441Srpaulo uint32_t isr2; /* isr status register LMPM_NIC_ISR2: 4951286441Srpaulo * enc_flag */ 4952286441Srpaulo uint32_t isr3; /* isr status register LMPM_NIC_ISR3: 4953286441Srpaulo * time_flag */ 4954286441Srpaulo uint32_t isr4; /* isr status register LMPM_NIC_ISR4: 4955286441Srpaulo * wico interrupt */ 4956303628Ssbruno uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ 4957286441Srpaulo uint32_t wait_event; /* wait event() caller address */ 4958286441Srpaulo uint32_t l2p_control; /* L2pControlField */ 4959286441Srpaulo uint32_t l2p_duration; /* L2pDurationField */ 4960286441Srpaulo uint32_t l2p_mhvalid; /* L2pMhValidBits */ 4961286441Srpaulo uint32_t l2p_addr_match; /* L2pAddrMatchStat */ 4962286441Srpaulo uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on 4963286441Srpaulo * (LMPM_PMG_SEL) */ 4964286441Srpaulo uint32_t u_timestamp; /* indicate when the date and time of the 4965286441Srpaulo * compilation */ 4966286441Srpaulo uint32_t flow_handler; /* FH read/write pointers, RX credit */ 4967303628Ssbruno} __packed /* LOG_ERROR_TABLE_API_S_VER_3 */; 4968303628Ssbruno 4969303628Ssbruno/* 4970303628Ssbruno * UMAC error struct - relevant starting from family 8000 chip. 4971303628Ssbruno * Note: This structure is read from the device with IO accesses, 4972303628Ssbruno * and the reading already does the endian conversion. As it is 4973303628Ssbruno * read with u32-sized accesses, any members with a different size 4974303628Ssbruno * need to be ordered correctly though! 4975303628Ssbruno */ 4976303628Ssbrunostruct iwm_umac_error_event_table { 4977303628Ssbruno uint32_t valid; /* (nonzero) valid, (0) log is empty */ 4978303628Ssbruno uint32_t error_id; /* type of error */ 4979303628Ssbruno uint32_t blink1; /* branch link */ 4980303628Ssbruno uint32_t blink2; /* branch link */ 4981303628Ssbruno uint32_t ilink1; /* interrupt link */ 4982303628Ssbruno uint32_t ilink2; /* interrupt link */ 4983303628Ssbruno uint32_t data1; /* error-specific data */ 4984303628Ssbruno uint32_t data2; /* error-specific data */ 4985303628Ssbruno uint32_t data3; /* error-specific data */ 4986303628Ssbruno uint32_t umac_major; 4987303628Ssbruno uint32_t umac_minor; 4988303628Ssbruno uint32_t frame_pointer; /* core register 27*/ 4989303628Ssbruno uint32_t stack_pointer; /* core register 28 */ 4990303628Ssbruno uint32_t cmd_header; /* latest host cmd sent to UMAC */ 4991303628Ssbruno uint32_t nic_isr_pref; /* ISR status register */ 4992286441Srpaulo} __packed; 4993286441Srpaulo 4994286441Srpaulo#define ERROR_START_OFFSET (1 * sizeof(uint32_t)) 4995286441Srpaulo#define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) 4996286441Srpaulo 4997286441Srpaulo#ifdef IWM_DEBUG 4998286441Srpaulostruct { 4999286441Srpaulo const char *name; 5000286441Srpaulo uint8_t num; 5001286441Srpaulo} advanced_lookup[] = { 5002286441Srpaulo { "NMI_INTERRUPT_WDG", 0x34 }, 5003286441Srpaulo { "SYSASSERT", 0x35 }, 5004286441Srpaulo { "UCODE_VERSION_MISMATCH", 0x37 }, 5005286441Srpaulo { "BAD_COMMAND", 0x38 }, 5006286441Srpaulo { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, 5007286441Srpaulo { "FATAL_ERROR", 0x3D }, 5008286441Srpaulo { "NMI_TRM_HW_ERR", 0x46 }, 5009286441Srpaulo { "NMI_INTERRUPT_TRM", 0x4C }, 5010286441Srpaulo { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, 5011286441Srpaulo { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, 5012286441Srpaulo { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, 5013286441Srpaulo { "NMI_INTERRUPT_HOST", 0x66 }, 5014286441Srpaulo { "NMI_INTERRUPT_ACTION_PT", 0x7C }, 5015286441Srpaulo { "NMI_INTERRUPT_UNKNOWN", 0x84 }, 5016286441Srpaulo { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, 5017286441Srpaulo { "ADVANCED_SYSASSERT", 0 }, 5018286441Srpaulo}; 5019286441Srpaulo 5020286441Srpaulostatic const char * 5021286441Srpauloiwm_desc_lookup(uint32_t num) 5022286441Srpaulo{ 5023286441Srpaulo int i; 5024286441Srpaulo 5025286441Srpaulo for (i = 0; i < nitems(advanced_lookup) - 1; i++) 5026286441Srpaulo if (advanced_lookup[i].num == num) 5027286441Srpaulo return advanced_lookup[i].name; 5028286441Srpaulo 5029286441Srpaulo /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ 5030286441Srpaulo return advanced_lookup[i].name; 5031286441Srpaulo} 5032286441Srpaulo 5033303628Ssbrunostatic void 5034303628Ssbrunoiwm_nic_umac_error(struct iwm_softc *sc) 5035303628Ssbruno{ 5036303628Ssbruno struct iwm_umac_error_event_table table; 5037303628Ssbruno uint32_t base; 5038303628Ssbruno 5039303628Ssbruno base = sc->sc_uc.uc_umac_error_event_table; 5040303628Ssbruno 5041303628Ssbruno if (base < 0x800000) { 5042303628Ssbruno device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", 5043303628Ssbruno base); 5044303628Ssbruno return; 5045303628Ssbruno } 5046303628Ssbruno 5047303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 5048303628Ssbruno device_printf(sc->sc_dev, "reading errlog failed\n"); 5049303628Ssbruno return; 5050303628Ssbruno } 5051303628Ssbruno 5052303628Ssbruno if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 5053303628Ssbruno device_printf(sc->sc_dev, "Start UMAC Error Log Dump:\n"); 5054303628Ssbruno device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 5055303628Ssbruno sc->sc_flags, table.valid); 5056303628Ssbruno } 5057303628Ssbruno 5058303628Ssbruno device_printf(sc->sc_dev, "0x%08X | %s\n", table.error_id, 5059303628Ssbruno iwm_desc_lookup(table.error_id)); 5060303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink1\n", table.blink1); 5061303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac branchlink2\n", table.blink2); 5062303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink1\n", 5063303628Ssbruno table.ilink1); 5064303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac interruptlink2\n", 5065303628Ssbruno table.ilink2); 5066303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data1\n", table.data1); 5067303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data2\n", table.data2); 5068303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac data3\n", table.data3); 5069303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac major\n", table.umac_major); 5070303628Ssbruno device_printf(sc->sc_dev, "0x%08X | umac minor\n", table.umac_minor); 5071303628Ssbruno device_printf(sc->sc_dev, "0x%08X | frame pointer\n", 5072303628Ssbruno table.frame_pointer); 5073303628Ssbruno device_printf(sc->sc_dev, "0x%08X | stack pointer\n", 5074303628Ssbruno table.stack_pointer); 5075303628Ssbruno device_printf(sc->sc_dev, "0x%08X | last host cmd\n", table.cmd_header); 5076303628Ssbruno device_printf(sc->sc_dev, "0x%08X | isr status reg\n", 5077303628Ssbruno table.nic_isr_pref); 5078303628Ssbruno} 5079303628Ssbruno 5080286441Srpaulo/* 5081286441Srpaulo * Support for dumping the error log seemed like a good idea ... 5082286441Srpaulo * but it's mostly hex junk and the only sensible thing is the 5083286441Srpaulo * hw/ucode revision (which we know anyway). Since it's here, 5084286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to 5085286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later. 5086286441Srpaulo */ 5087286441Srpaulostatic void 5088286441Srpauloiwm_nic_error(struct iwm_softc *sc) 5089286441Srpaulo{ 5090286441Srpaulo struct iwm_error_event_table table; 5091286441Srpaulo uint32_t base; 5092286441Srpaulo 5093286441Srpaulo device_printf(sc->sc_dev, "dumping device error log\n"); 5094286441Srpaulo base = sc->sc_uc.uc_error_event_table; 5095303628Ssbruno if (base < 0x800000) { 5096286441Srpaulo device_printf(sc->sc_dev, 5097303628Ssbruno "Invalid error log pointer 0x%08x\n", base); 5098286441Srpaulo return; 5099286441Srpaulo } 5100286441Srpaulo 5101303628Ssbruno if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { 5102286441Srpaulo device_printf(sc->sc_dev, "reading errlog failed\n"); 5103286441Srpaulo return; 5104286441Srpaulo } 5105286441Srpaulo 5106286441Srpaulo if (!table.valid) { 5107286441Srpaulo device_printf(sc->sc_dev, "errlog not found, skipping\n"); 5108286441Srpaulo return; 5109286441Srpaulo } 5110286441Srpaulo 5111286441Srpaulo if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 5112303628Ssbruno device_printf(sc->sc_dev, "Start Error Log Dump:\n"); 5113286441Srpaulo device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 5114286441Srpaulo sc->sc_flags, table.valid); 5115286441Srpaulo } 5116286441Srpaulo 5117286441Srpaulo device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id, 5118303628Ssbruno iwm_desc_lookup(table.error_id)); 5119303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status0\n", 5120303628Ssbruno table.trm_hw_status0); 5121303628Ssbruno device_printf(sc->sc_dev, "%08X | trm_hw_status1\n", 5122303628Ssbruno table.trm_hw_status1); 5123286441Srpaulo device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2); 5124286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1); 5125286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2); 5126286441Srpaulo device_printf(sc->sc_dev, "%08X | data1\n", table.data1); 5127286441Srpaulo device_printf(sc->sc_dev, "%08X | data2\n", table.data2); 5128286441Srpaulo device_printf(sc->sc_dev, "%08X | data3\n", table.data3); 5129286441Srpaulo device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time); 5130286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low); 5131286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi); 5132286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1); 5133286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2); 5134303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode revision type\n", 5135303628Ssbruno table.fw_rev_type); 5136303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version major\n", table.major); 5137303628Ssbruno device_printf(sc->sc_dev, "%08X | uCode version minor\n", table.minor); 5138286441Srpaulo device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver); 5139286441Srpaulo device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver); 5140286441Srpaulo device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd); 5141286441Srpaulo device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0); 5142286441Srpaulo device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1); 5143286441Srpaulo device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2); 5144286441Srpaulo device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3); 5145286441Srpaulo device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4); 5146303628Ssbruno device_printf(sc->sc_dev, "%08X | last cmd Id\n", table.last_cmd_id); 5147286441Srpaulo device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event); 5148286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control); 5149286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration); 5150286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid); 5151286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match); 5152286441Srpaulo device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); 5153286441Srpaulo device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); 5154286441Srpaulo device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); 5155303628Ssbruno 5156303628Ssbruno if (sc->sc_uc.uc_umac_error_event_table) 5157303628Ssbruno iwm_nic_umac_error(sc); 5158286441Srpaulo} 5159286441Srpaulo#endif 5160286441Srpaulo 5161286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); 5162286441Srpaulo 5163286441Srpaulo/* 5164286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt. 5165286441Srpaulo * Basic structure from if_iwn 5166286441Srpaulo */ 5167286441Srpaulostatic void 5168286441Srpauloiwm_notif_intr(struct iwm_softc *sc) 5169286441Srpaulo{ 5170303628Ssbruno struct ieee80211com *ic = &sc->sc_ic; 5171286441Srpaulo uint16_t hw; 5172286441Srpaulo 5173286441Srpaulo bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 5174286441Srpaulo BUS_DMASYNC_POSTREAD); 5175286441Srpaulo 5176286441Srpaulo hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; 5177303628Ssbruno 5178303628Ssbruno /* 5179303628Ssbruno * Process responses 5180303628Ssbruno */ 5181286441Srpaulo while (sc->rxq.cur != hw) { 5182286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 5183330158Seadler struct iwm_rx_data *data = &ring->data[ring->cur]; 5184286441Srpaulo struct iwm_rx_packet *pkt; 5185286441Srpaulo struct iwm_cmd_response *cresp; 5186303628Ssbruno int qid, idx, code; 5187286441Srpaulo 5188330158Seadler bus_dmamap_sync(ring->data_dmat, data->map, 5189286441Srpaulo BUS_DMASYNC_POSTREAD); 5190286441Srpaulo pkt = mtod(data->m, struct iwm_rx_packet *); 5191286441Srpaulo 5192286441Srpaulo qid = pkt->hdr.qid & ~0x80; 5193286441Srpaulo idx = pkt->hdr.idx; 5194286441Srpaulo 5195303628Ssbruno code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); 5196286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5197303628Ssbruno "rx packet qid=%d idx=%d type=%x %d %d\n", 5198330158Seadler pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, ring->cur, hw); 5199286441Srpaulo 5200286441Srpaulo /* 5201286441Srpaulo * randomly get these from the firmware, no idea why. 5202286441Srpaulo * they at least seem harmless, so just ignore them for now 5203286441Srpaulo */ 5204286441Srpaulo if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) 5205286441Srpaulo || pkt->len_n_flags == htole32(0x55550000))) { 5206286441Srpaulo ADVANCE_RXQ(sc); 5207286441Srpaulo continue; 5208286441Srpaulo } 5209286441Srpaulo 5210303628Ssbruno switch (code) { 5211286441Srpaulo case IWM_REPLY_RX_PHY_CMD: 5212286441Srpaulo iwm_mvm_rx_rx_phy_cmd(sc, pkt, data); 5213286441Srpaulo break; 5214286441Srpaulo 5215286441Srpaulo case IWM_REPLY_RX_MPDU_CMD: 5216286441Srpaulo iwm_mvm_rx_rx_mpdu(sc, pkt, data); 5217286441Srpaulo break; 5218286441Srpaulo 5219286441Srpaulo case IWM_TX_CMD: 5220286441Srpaulo iwm_mvm_rx_tx_cmd(sc, pkt, data); 5221286441Srpaulo break; 5222286441Srpaulo 5223286441Srpaulo case IWM_MISSED_BEACONS_NOTIFICATION: { 5224286441Srpaulo struct iwm_missed_beacons_notif *resp; 5225286441Srpaulo int missed; 5226286441Srpaulo 5227286441Srpaulo /* XXX look at mac_id to determine interface ID */ 5228286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5229286441Srpaulo 5230330158Seadler resp = (void *)pkt->data; 5231286441Srpaulo missed = le32toh(resp->consec_missed_beacons); 5232286441Srpaulo 5233286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE, 5234286441Srpaulo "%s: MISSED_BEACON: mac_id=%d, " 5235286441Srpaulo "consec_since_last_rx=%d, consec=%d, num_expect=%d " 5236286441Srpaulo "num_rx=%d\n", 5237286441Srpaulo __func__, 5238286441Srpaulo le32toh(resp->mac_id), 5239286441Srpaulo le32toh(resp->consec_missed_beacons_since_last_rx), 5240286441Srpaulo le32toh(resp->consec_missed_beacons), 5241286441Srpaulo le32toh(resp->num_expected_beacons), 5242286441Srpaulo le32toh(resp->num_recvd_beacons)); 5243286441Srpaulo 5244286441Srpaulo /* Be paranoid */ 5245286441Srpaulo if (vap == NULL) 5246286441Srpaulo break; 5247286441Srpaulo 5248286441Srpaulo /* XXX no net80211 locking? */ 5249286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && 5250286441Srpaulo (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 5251286441Srpaulo if (missed > vap->iv_bmissthreshold) { 5252286441Srpaulo /* XXX bad locking; turn into task */ 5253286441Srpaulo IWM_UNLOCK(sc); 5254286441Srpaulo ieee80211_beacon_miss(ic); 5255286441Srpaulo IWM_LOCK(sc); 5256286441Srpaulo } 5257286441Srpaulo } 5258286441Srpaulo 5259286441Srpaulo break; } 5260286441Srpaulo 5261303628Ssbruno case IWM_MFUART_LOAD_NOTIFICATION: 5262303628Ssbruno break; 5263303628Ssbruno 5264286441Srpaulo case IWM_MVM_ALIVE: { 5265303628Ssbruno struct iwm_mvm_alive_resp_v1 *resp1; 5266303628Ssbruno struct iwm_mvm_alive_resp_v2 *resp2; 5267303628Ssbruno struct iwm_mvm_alive_resp_v3 *resp3; 5268286441Srpaulo 5269303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { 5270330158Seadler resp1 = (void *)pkt->data; 5271303628Ssbruno sc->sc_uc.uc_error_event_table 5272303628Ssbruno = le32toh(resp1->error_event_table_ptr); 5273303628Ssbruno sc->sc_uc.uc_log_event_table 5274303628Ssbruno = le32toh(resp1->log_event_table_ptr); 5275303628Ssbruno sc->sched_base = le32toh(resp1->scd_base_ptr); 5276303628Ssbruno if (resp1->status == IWM_ALIVE_STATUS_OK) 5277303628Ssbruno sc->sc_uc.uc_ok = 1; 5278303628Ssbruno else 5279303628Ssbruno sc->sc_uc.uc_ok = 0; 5280303628Ssbruno } 5281286441Srpaulo 5282303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { 5283330158Seadler resp2 = (void *)pkt->data; 5284303628Ssbruno sc->sc_uc.uc_error_event_table 5285303628Ssbruno = le32toh(resp2->error_event_table_ptr); 5286303628Ssbruno sc->sc_uc.uc_log_event_table 5287303628Ssbruno = le32toh(resp2->log_event_table_ptr); 5288303628Ssbruno sc->sched_base = le32toh(resp2->scd_base_ptr); 5289303628Ssbruno sc->sc_uc.uc_umac_error_event_table 5290303628Ssbruno = le32toh(resp2->error_info_addr); 5291303628Ssbruno if (resp2->status == IWM_ALIVE_STATUS_OK) 5292303628Ssbruno sc->sc_uc.uc_ok = 1; 5293303628Ssbruno else 5294303628Ssbruno sc->sc_uc.uc_ok = 0; 5295303628Ssbruno } 5296303628Ssbruno 5297303628Ssbruno if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { 5298330158Seadler resp3 = (void *)pkt->data; 5299303628Ssbruno sc->sc_uc.uc_error_event_table 5300303628Ssbruno = le32toh(resp3->error_event_table_ptr); 5301303628Ssbruno sc->sc_uc.uc_log_event_table 5302303628Ssbruno = le32toh(resp3->log_event_table_ptr); 5303303628Ssbruno sc->sched_base = le32toh(resp3->scd_base_ptr); 5304303628Ssbruno sc->sc_uc.uc_umac_error_event_table 5305303628Ssbruno = le32toh(resp3->error_info_addr); 5306303628Ssbruno if (resp3->status == IWM_ALIVE_STATUS_OK) 5307303628Ssbruno sc->sc_uc.uc_ok = 1; 5308303628Ssbruno else 5309303628Ssbruno sc->sc_uc.uc_ok = 0; 5310303628Ssbruno } 5311303628Ssbruno 5312286441Srpaulo sc->sc_uc.uc_intr = 1; 5313286441Srpaulo wakeup(&sc->sc_uc); 5314286441Srpaulo break; } 5315286441Srpaulo 5316286441Srpaulo case IWM_CALIB_RES_NOTIF_PHY_DB: { 5317286441Srpaulo struct iwm_calib_res_notif_phy_db *phy_db_notif; 5318330158Seadler phy_db_notif = (void *)pkt->data; 5319286441Srpaulo 5320330163Seadler iwm_phy_db_set_section(sc->sc_phy_db, phy_db_notif); 5321286441Srpaulo 5322286441Srpaulo break; } 5323286441Srpaulo 5324286441Srpaulo case IWM_STATISTICS_NOTIFICATION: { 5325286441Srpaulo struct iwm_notif_statistics *stats; 5326330158Seadler stats = (void *)pkt->data; 5327286441Srpaulo memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); 5328330144Seadler sc->sc_noise = iwm_get_noise(sc, &stats->rx.general); 5329286441Srpaulo break; } 5330286441Srpaulo 5331286441Srpaulo case IWM_NVM_ACCESS_CMD: 5332303628Ssbruno case IWM_MCC_UPDATE_CMD: 5333286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 5334286441Srpaulo memcpy(sc->sc_cmd_resp, 5335286441Srpaulo pkt, sizeof(sc->sc_cmd_resp)); 5336286441Srpaulo } 5337286441Srpaulo break; 5338286441Srpaulo 5339303628Ssbruno case IWM_MCC_CHUB_UPDATE_CMD: { 5340303628Ssbruno struct iwm_mcc_chub_notif *notif; 5341330158Seadler notif = (void *)pkt->data; 5342303628Ssbruno 5343303628Ssbruno sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; 5344303628Ssbruno sc->sc_fw_mcc[1] = notif->mcc & 0xff; 5345303628Ssbruno sc->sc_fw_mcc[2] = '\0'; 5346303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_RESET, 5347303628Ssbruno "fw source %d sent CC '%s'\n", 5348303628Ssbruno notif->source_id, sc->sc_fw_mcc); 5349303628Ssbruno break; } 5350303628Ssbruno 5351303628Ssbruno case IWM_DTS_MEASUREMENT_NOTIFICATION: 5352303628Ssbruno break; 5353303628Ssbruno 5354286441Srpaulo case IWM_PHY_CONFIGURATION_CMD: 5355286441Srpaulo case IWM_TX_ANT_CONFIGURATION_CMD: 5356286441Srpaulo case IWM_ADD_STA: 5357286441Srpaulo case IWM_MAC_CONTEXT_CMD: 5358286441Srpaulo case IWM_REPLY_SF_CFG_CMD: 5359286441Srpaulo case IWM_POWER_TABLE_CMD: 5360286441Srpaulo case IWM_PHY_CONTEXT_CMD: 5361286441Srpaulo case IWM_BINDING_CONTEXT_CMD: 5362286441Srpaulo case IWM_TIME_EVENT_CMD: 5363303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_CFG_CMD): 5364303628Ssbruno case IWM_WIDE_ID(IWM_ALWAYS_LONG_GROUP, IWM_SCAN_REQ_UMAC): 5365303628Ssbruno case IWM_SCAN_OFFLOAD_REQUEST_CMD: 5366286441Srpaulo case IWM_REPLY_BEACON_FILTERING_CMD: 5367286441Srpaulo case IWM_MAC_PM_POWER_TABLE: 5368286441Srpaulo case IWM_TIME_QUOTA_CMD: 5369286441Srpaulo case IWM_REMOVE_STA: 5370286441Srpaulo case IWM_TXPATH_FLUSH: 5371286441Srpaulo case IWM_LQ_CMD: 5372303628Ssbruno case IWM_BT_CONFIG: 5373303628Ssbruno case IWM_REPLY_THERMAL_MNG_BACKOFF: 5374330158Seadler cresp = (void *)pkt->data; 5375286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 5376286441Srpaulo memcpy(sc->sc_cmd_resp, 5377286441Srpaulo pkt, sizeof(*pkt)+sizeof(*cresp)); 5378286441Srpaulo } 5379286441Srpaulo break; 5380286441Srpaulo 5381286441Srpaulo /* ignore */ 5382286441Srpaulo case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */ 5383286441Srpaulo break; 5384286441Srpaulo 5385286441Srpaulo case IWM_INIT_COMPLETE_NOTIF: 5386286441Srpaulo sc->sc_init_complete = 1; 5387286441Srpaulo wakeup(&sc->sc_init_complete); 5388286441Srpaulo break; 5389286441Srpaulo 5390303628Ssbruno case IWM_SCAN_OFFLOAD_COMPLETE: { 5391303628Ssbruno struct iwm_periodic_scan_complete *notif; 5392330158Seadler notif = (void *)pkt->data; 5393303628Ssbruno break; 5394303628Ssbruno } 5395303628Ssbruno 5396303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE: { 5397303628Ssbruno struct iwm_lmac_scan_complete_notif *notif; 5398330158Seadler notif = (void *)pkt->data; 5399303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5400303628Ssbruno break; 5401303628Ssbruno } 5402303628Ssbruno 5403303628Ssbruno case IWM_SCAN_COMPLETE_UMAC: { 5404303628Ssbruno struct iwm_umac_scan_complete *notif; 5405330158Seadler notif = (void *)pkt->data; 5406303628Ssbruno 5407303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, 5408303628Ssbruno "UMAC scan complete, status=0x%x\n", 5409303628Ssbruno notif->status); 5410303628Ssbruno#if 0 /* XXX This would be a duplicate scan end call */ 5411286441Srpaulo taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task); 5412303628Ssbruno#endif 5413303628Ssbruno break; 5414303628Ssbruno } 5415286441Srpaulo 5416303628Ssbruno case IWM_SCAN_ITERATION_COMPLETE_UMAC: { 5417303628Ssbruno struct iwm_umac_scan_iter_complete_notif *notif; 5418330158Seadler notif = (void *)pkt->data; 5419303628Ssbruno 5420303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "UMAC scan iteration " 5421303628Ssbruno "complete, status=0x%x, %d channels scanned\n", 5422303628Ssbruno notif->status, notif->scanned_channels); 5423303628Ssbruno ieee80211_runtask(&sc->sc_ic, &sc->sc_es_task); 5424303628Ssbruno break; 5425303628Ssbruno } 5426303628Ssbruno 5427286441Srpaulo case IWM_REPLY_ERROR: { 5428286441Srpaulo struct iwm_error_resp *resp; 5429330158Seadler resp = (void *)pkt->data; 5430286441Srpaulo 5431286441Srpaulo device_printf(sc->sc_dev, 5432286441Srpaulo "firmware error 0x%x, cmd 0x%x\n", 5433286441Srpaulo le32toh(resp->error_type), 5434286441Srpaulo resp->cmd_id); 5435303628Ssbruno break; 5436303628Ssbruno } 5437286441Srpaulo 5438286441Srpaulo case IWM_TIME_EVENT_NOTIFICATION: { 5439286441Srpaulo struct iwm_time_event_notif *notif; 5440330158Seadler notif = (void *)pkt->data; 5441286441Srpaulo 5442286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5443300833Sadrian "TE notif status = 0x%x action = 0x%x\n", 5444303628Ssbruno notif->status, notif->action); 5445303628Ssbruno break; 5446303628Ssbruno } 5447286441Srpaulo 5448286441Srpaulo case IWM_MCAST_FILTER_CMD: 5449286441Srpaulo break; 5450286441Srpaulo 5451303628Ssbruno case IWM_SCD_QUEUE_CFG: { 5452303628Ssbruno struct iwm_scd_txq_cfg_rsp *rsp; 5453330158Seadler rsp = (void *)pkt->data; 5454303628Ssbruno 5455303628Ssbruno IWM_DPRINTF(sc, IWM_DEBUG_CMD, 5456303628Ssbruno "queue cfg token=0x%x sta_id=%d " 5457303628Ssbruno "tid=%d scd_queue=%d\n", 5458303628Ssbruno rsp->token, rsp->sta_id, rsp->tid, 5459303628Ssbruno rsp->scd_queue); 5460303628Ssbruno break; 5461303628Ssbruno } 5462303628Ssbruno 5463286441Srpaulo default: 5464286441Srpaulo device_printf(sc->sc_dev, 5465286441Srpaulo "frame %d/%d %x UNHANDLED (this should " 5466286441Srpaulo "not happen)\n", qid, idx, 5467286441Srpaulo pkt->len_n_flags); 5468286441Srpaulo break; 5469286441Srpaulo } 5470286441Srpaulo 5471286441Srpaulo /* 5472286441Srpaulo * Why test bit 0x80? The Linux driver: 5473286441Srpaulo * 5474286441Srpaulo * There is one exception: uCode sets bit 15 when it 5475286441Srpaulo * originates the response/notification, i.e. when the 5476286441Srpaulo * response/notification is not a direct response to a 5477286441Srpaulo * command sent by the driver. For example, uCode issues 5478286441Srpaulo * IWM_REPLY_RX when it sends a received frame to the driver; 5479286441Srpaulo * it is not a direct response to any driver command. 5480286441Srpaulo * 5481286441Srpaulo * Ok, so since when is 7 == 15? Well, the Linux driver 5482286441Srpaulo * uses a slightly different format for pkt->hdr, and "qid" 5483286441Srpaulo * is actually the upper byte of a two-byte field. 5484286441Srpaulo */ 5485286441Srpaulo if (!(pkt->hdr.qid & (1 << 7))) { 5486286441Srpaulo iwm_cmd_done(sc, pkt); 5487286441Srpaulo } 5488286441Srpaulo 5489286441Srpaulo ADVANCE_RXQ(sc); 5490286441Srpaulo } 5491286441Srpaulo 5492286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 5493286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 5494286441Srpaulo 5495286441Srpaulo /* 5496286441Srpaulo * Tell the firmware what we have processed. 5497286441Srpaulo * Seems like the hardware gets upset unless we align 5498286441Srpaulo * the write by 8?? 5499286441Srpaulo */ 5500286441Srpaulo hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; 5501286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); 5502286441Srpaulo} 5503286441Srpaulo 5504286441Srpaulostatic void 5505286441Srpauloiwm_intr(void *arg) 5506286441Srpaulo{ 5507286441Srpaulo struct iwm_softc *sc = arg; 5508286441Srpaulo int handled = 0; 5509286441Srpaulo int r1, r2, rv = 0; 5510286441Srpaulo int isperiodic = 0; 5511286441Srpaulo 5512286441Srpaulo IWM_LOCK(sc); 5513286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 5514286441Srpaulo 5515286441Srpaulo if (sc->sc_flags & IWM_FLAG_USE_ICT) { 5516286441Srpaulo uint32_t *ict = sc->ict_dma.vaddr; 5517286441Srpaulo int tmp; 5518286441Srpaulo 5519286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5520286441Srpaulo if (!tmp) 5521286441Srpaulo goto out_ena; 5522286441Srpaulo 5523286441Srpaulo /* 5524286441Srpaulo * ok, there was something. keep plowing until we have all. 5525286441Srpaulo */ 5526286441Srpaulo r1 = r2 = 0; 5527286441Srpaulo while (tmp) { 5528286441Srpaulo r1 |= tmp; 5529286441Srpaulo ict[sc->ict_cur] = 0; 5530286441Srpaulo sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT; 5531286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 5532286441Srpaulo } 5533286441Srpaulo 5534286441Srpaulo /* this is where the fun begins. don't ask */ 5535286441Srpaulo if (r1 == 0xffffffff) 5536286441Srpaulo r1 = 0; 5537286441Srpaulo 5538286441Srpaulo /* i am not expected to understand this */ 5539286441Srpaulo if (r1 & 0xc0000) 5540286441Srpaulo r1 |= 0x8000; 5541286441Srpaulo r1 = (0xff & r1) | ((0xff00 & r1) << 16); 5542286441Srpaulo } else { 5543286441Srpaulo r1 = IWM_READ(sc, IWM_CSR_INT); 5544286441Srpaulo /* "hardware gone" (where, fishing?) */ 5545286441Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 5546286441Srpaulo goto out; 5547286441Srpaulo r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); 5548286441Srpaulo } 5549286441Srpaulo if (r1 == 0 && r2 == 0) { 5550286441Srpaulo goto out_ena; 5551286441Srpaulo } 5552286441Srpaulo 5553286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); 5554286441Srpaulo 5555286441Srpaulo /* ignored */ 5556286441Srpaulo handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); 5557286441Srpaulo 5558286441Srpaulo if (r1 & IWM_CSR_INT_BIT_SW_ERR) { 5559286441Srpaulo int i; 5560287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5561286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 5562286441Srpaulo 5563298793Sdchagin#ifdef IWM_DEBUG 5564286441Srpaulo iwm_nic_error(sc); 5565298793Sdchagin#endif 5566286441Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 5567286441Srpaulo device_printf(sc->sc_dev, "driver status:\n"); 5568286441Srpaulo for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) { 5569286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[i]; 5570286441Srpaulo device_printf(sc->sc_dev, 5571286441Srpaulo " tx ring %2d: qid=%-2d cur=%-3d " 5572286441Srpaulo "queued=%-3d\n", 5573286441Srpaulo i, ring->qid, ring->cur, ring->queued); 5574286441Srpaulo } 5575286441Srpaulo device_printf(sc->sc_dev, 5576286441Srpaulo " rx ring: cur=%d\n", sc->rxq.cur); 5577286441Srpaulo device_printf(sc->sc_dev, 5578298659Scem " 802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state); 5579286441Srpaulo 5580298594Sadrian /* Don't stop the device; just do a VAP restart */ 5581298594Sadrian IWM_UNLOCK(sc); 5582286441Srpaulo 5583298594Sadrian if (vap == NULL) { 5584298594Sadrian printf("%s: null vap\n", __func__); 5585298594Sadrian return; 5586298594Sadrian } 5587298594Sadrian 5588298594Sadrian device_printf(sc->sc_dev, "%s: controller panicked, iv_state = %d; " 5589298594Sadrian "restarting\n", __func__, vap->iv_state); 5590298594Sadrian 5591298594Sadrian /* XXX TODO: turn this into a callout/taskqueue */ 5592298594Sadrian ieee80211_restart_all(ic); 5593298594Sadrian return; 5594286441Srpaulo } 5595286441Srpaulo 5596286441Srpaulo if (r1 & IWM_CSR_INT_BIT_HW_ERR) { 5597286441Srpaulo handled |= IWM_CSR_INT_BIT_HW_ERR; 5598286441Srpaulo device_printf(sc->sc_dev, "hardware error, stopping device\n"); 5599287197Sglebius iwm_stop(sc); 5600286441Srpaulo rv = 1; 5601286441Srpaulo goto out; 5602286441Srpaulo } 5603286441Srpaulo 5604286441Srpaulo /* firmware chunk loaded */ 5605286441Srpaulo if (r1 & IWM_CSR_INT_BIT_FH_TX) { 5606286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); 5607286441Srpaulo handled |= IWM_CSR_INT_BIT_FH_TX; 5608286441Srpaulo sc->sc_fw_chunk_done = 1; 5609286441Srpaulo wakeup(&sc->sc_fw); 5610286441Srpaulo } 5611286441Srpaulo 5612286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RF_KILL) { 5613286441Srpaulo handled |= IWM_CSR_INT_BIT_RF_KILL; 5614287197Sglebius if (iwm_check_rfkill(sc)) { 5615286441Srpaulo device_printf(sc->sc_dev, 5616286441Srpaulo "%s: rfkill switch, disabling interface\n", 5617286441Srpaulo __func__); 5618287197Sglebius iwm_stop(sc); 5619286441Srpaulo } 5620286441Srpaulo } 5621286441Srpaulo 5622286441Srpaulo /* 5623286441Srpaulo * The Linux driver uses periodic interrupts to avoid races. 5624286441Srpaulo * We cargo-cult like it's going out of fashion. 5625286441Srpaulo */ 5626286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { 5627286441Srpaulo handled |= IWM_CSR_INT_BIT_RX_PERIODIC; 5628286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); 5629286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) 5630286441Srpaulo IWM_WRITE_1(sc, 5631286441Srpaulo IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); 5632286441Srpaulo isperiodic = 1; 5633286441Srpaulo } 5634286441Srpaulo 5635286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) { 5636286441Srpaulo handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX); 5637286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); 5638286441Srpaulo 5639286441Srpaulo iwm_notif_intr(sc); 5640286441Srpaulo 5641286441Srpaulo /* enable periodic interrupt, see above */ 5642286441Srpaulo if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic) 5643286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, 5644286441Srpaulo IWM_CSR_INT_PERIODIC_ENA); 5645286441Srpaulo } 5646286441Srpaulo 5647286441Srpaulo if (__predict_false(r1 & ~handled)) 5648286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 5649286441Srpaulo "%s: unhandled interrupts: %x\n", __func__, r1); 5650286441Srpaulo rv = 1; 5651286441Srpaulo 5652286441Srpaulo out_ena: 5653286441Srpaulo iwm_restore_interrupts(sc); 5654286441Srpaulo out: 5655286441Srpaulo IWM_UNLOCK(sc); 5656286441Srpaulo return; 5657286441Srpaulo} 5658286441Srpaulo 5659286441Srpaulo/* 5660286441Srpaulo * Autoconf glue-sniffing 5661286441Srpaulo */ 5662286441Srpaulo#define PCI_VENDOR_INTEL 0x8086 5663286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_1 0x08b3 5664286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_2 0x08b4 5665303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_1 0x3165 5666303628Ssbruno#define PCI_PRODUCT_INTEL_WL_3165_2 0x3166 5667286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_1 0x08b1 5668286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_2 0x08b2 5669286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_1 0x095a 5670286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_2 0x095b 5671303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_1 0x24f3 5672303628Ssbruno#define PCI_PRODUCT_INTEL_WL_8260_2 0x24f4 5673286441Srpaulo 5674286441Srpaulostatic const struct iwm_devices { 5675286441Srpaulo uint16_t device; 5676286441Srpaulo const char *name; 5677286441Srpaulo} iwm_devices[] = { 5678286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" }, 5679286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" }, 5680303628Ssbruno { PCI_PRODUCT_INTEL_WL_3165_1, "Intel Dual Band Wireless AC 3165" }, 5681303628Ssbruno { PCI_PRODUCT_INTEL_WL_3165_2, "Intel Dual Band Wireless AC 3165" }, 5682286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" }, 5683286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" }, 5684286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" }, 5685286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" }, 5686303628Ssbruno { PCI_PRODUCT_INTEL_WL_8260_1, "Intel Dual Band Wireless AC 8260" }, 5687303628Ssbruno { PCI_PRODUCT_INTEL_WL_8260_2, "Intel Dual Band Wireless AC 8260" }, 5688286441Srpaulo}; 5689286441Srpaulo 5690286441Srpaulostatic int 5691286441Srpauloiwm_probe(device_t dev) 5692286441Srpaulo{ 5693286441Srpaulo int i; 5694286441Srpaulo 5695303628Ssbruno for (i = 0; i < nitems(iwm_devices); i++) { 5696286441Srpaulo if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && 5697286441Srpaulo pci_get_device(dev) == iwm_devices[i].device) { 5698286441Srpaulo device_set_desc(dev, iwm_devices[i].name); 5699286441Srpaulo return (BUS_PROBE_DEFAULT); 5700286441Srpaulo } 5701303628Ssbruno } 5702286441Srpaulo 5703286441Srpaulo return (ENXIO); 5704286441Srpaulo} 5705286441Srpaulo 5706286441Srpaulostatic int 5707286441Srpauloiwm_dev_check(device_t dev) 5708286441Srpaulo{ 5709286441Srpaulo struct iwm_softc *sc; 5710286441Srpaulo 5711286441Srpaulo sc = device_get_softc(dev); 5712286441Srpaulo 5713286441Srpaulo switch (pci_get_device(dev)) { 5714286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_1: 5715286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_2: 5716330166Seadler sc->cfg = &iwm3160_cfg; 5717303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5718286441Srpaulo return (0); 5719303628Ssbruno case PCI_PRODUCT_INTEL_WL_3165_1: 5720303628Ssbruno case PCI_PRODUCT_INTEL_WL_3165_2: 5721330166Seadler sc->cfg = &iwm3165_cfg; 5722303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5723303628Ssbruno return (0); 5724286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_1: 5725286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_2: 5726330166Seadler sc->cfg = &iwm7260_cfg; 5727303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5728286441Srpaulo return (0); 5729286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_1: 5730286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_2: 5731330166Seadler sc->cfg = &iwm7265_cfg; 5732303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 5733286441Srpaulo return (0); 5734303628Ssbruno case PCI_PRODUCT_INTEL_WL_8260_1: 5735303628Ssbruno case PCI_PRODUCT_INTEL_WL_8260_2: 5736330166Seadler sc->cfg = &iwm8260_cfg; 5737303628Ssbruno sc->sc_fwdmasegsz = IWM_FWDMASEGSZ_8000; 5738303628Ssbruno return (0); 5739286441Srpaulo default: 5740286441Srpaulo device_printf(dev, "unknown adapter type\n"); 5741286441Srpaulo return ENXIO; 5742286441Srpaulo } 5743286441Srpaulo} 5744286441Srpaulo 5745330140Seadler/* PCI registers */ 5746330140Seadler#define PCI_CFG_RETRY_TIMEOUT 0x041 5747330140Seadler 5748286441Srpaulostatic int 5749286441Srpauloiwm_pci_attach(device_t dev) 5750286441Srpaulo{ 5751286441Srpaulo struct iwm_softc *sc; 5752286441Srpaulo int count, error, rid; 5753286441Srpaulo uint16_t reg; 5754286441Srpaulo 5755286441Srpaulo sc = device_get_softc(dev); 5756286441Srpaulo 5757330140Seadler /* We disable the RETRY_TIMEOUT register (0x41) to keep 5758330140Seadler * PCI Tx retries from interfering with C3 CPU state */ 5759330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 5760286441Srpaulo 5761286441Srpaulo /* Enable bus-mastering and hardware bug workaround. */ 5762286441Srpaulo pci_enable_busmaster(dev); 5763286441Srpaulo reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg)); 5764286441Srpaulo /* if !MSI */ 5765286441Srpaulo if (reg & PCIM_STATUS_INTxSTATE) { 5766286441Srpaulo reg &= ~PCIM_STATUS_INTxSTATE; 5767286441Srpaulo } 5768286441Srpaulo pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg)); 5769286441Srpaulo 5770286441Srpaulo rid = PCIR_BAR(0); 5771286441Srpaulo sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 5772286441Srpaulo RF_ACTIVE); 5773286441Srpaulo if (sc->sc_mem == NULL) { 5774286441Srpaulo device_printf(sc->sc_dev, "can't map mem space\n"); 5775286441Srpaulo return (ENXIO); 5776286441Srpaulo } 5777286441Srpaulo sc->sc_st = rman_get_bustag(sc->sc_mem); 5778286441Srpaulo sc->sc_sh = rman_get_bushandle(sc->sc_mem); 5779286441Srpaulo 5780286441Srpaulo /* Install interrupt handler. */ 5781286441Srpaulo count = 1; 5782286441Srpaulo rid = 0; 5783286441Srpaulo if (pci_alloc_msi(dev, &count) == 0) 5784286441Srpaulo rid = 1; 5785286441Srpaulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 5786286441Srpaulo (rid != 0 ? 0 : RF_SHAREABLE)); 5787286441Srpaulo if (sc->sc_irq == NULL) { 5788286441Srpaulo device_printf(dev, "can't map interrupt\n"); 5789286441Srpaulo return (ENXIO); 5790286441Srpaulo } 5791286441Srpaulo error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 5792286441Srpaulo NULL, iwm_intr, sc, &sc->sc_ih); 5793286441Srpaulo if (sc->sc_ih == NULL) { 5794286441Srpaulo device_printf(dev, "can't establish interrupt"); 5795286441Srpaulo return (ENXIO); 5796286441Srpaulo } 5797286441Srpaulo sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); 5798286441Srpaulo 5799286441Srpaulo return (0); 5800286441Srpaulo} 5801286441Srpaulo 5802286441Srpaulostatic void 5803286441Srpauloiwm_pci_detach(device_t dev) 5804286441Srpaulo{ 5805286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 5806286441Srpaulo 5807286441Srpaulo if (sc->sc_irq != NULL) { 5808286441Srpaulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 5809286441Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 5810286441Srpaulo rman_get_rid(sc->sc_irq), sc->sc_irq); 5811286441Srpaulo pci_release_msi(dev); 5812286441Srpaulo } 5813286441Srpaulo if (sc->sc_mem != NULL) 5814286441Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, 5815286441Srpaulo rman_get_rid(sc->sc_mem), sc->sc_mem); 5816286441Srpaulo} 5817286441Srpaulo 5818286441Srpaulo 5819286441Srpaulo 5820286441Srpaulostatic int 5821286441Srpauloiwm_attach(device_t dev) 5822286441Srpaulo{ 5823287197Sglebius struct iwm_softc *sc = device_get_softc(dev); 5824287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 5825286441Srpaulo int error; 5826286441Srpaulo int txq_i, i; 5827286441Srpaulo 5828286441Srpaulo sc->sc_dev = dev; 5829330164Seadler sc->sc_attached = 1; 5830293099Savos IWM_LOCK_INIT(sc); 5831287197Sglebius mbufq_init(&sc->sc_snd, ifqmaxlen); 5832286441Srpaulo callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); 5833301187Sadrian callout_init_mtx(&sc->sc_led_blink_to, &sc->sc_mtx, 0); 5834286441Srpaulo TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc); 5835286441Srpaulo 5836330163Seadler /* Init phy db */ 5837330163Seadler sc->sc_phy_db = iwm_phy_db_init(sc); 5838330163Seadler if (!sc->sc_phy_db) { 5839330163Seadler device_printf(dev, "Cannot init phy_db\n"); 5840330163Seadler goto fail; 5841330163Seadler } 5842330163Seadler 5843286441Srpaulo /* PCI attach */ 5844286441Srpaulo error = iwm_pci_attach(dev); 5845286441Srpaulo if (error != 0) 5846286441Srpaulo goto fail; 5847286441Srpaulo 5848286441Srpaulo sc->sc_wantresp = -1; 5849286441Srpaulo 5850286441Srpaulo /* Check device type */ 5851286441Srpaulo error = iwm_dev_check(dev); 5852286441Srpaulo if (error != 0) 5853286441Srpaulo goto fail; 5854286441Srpaulo 5855330166Seadler sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); 5856286441Srpaulo /* 5857303628Ssbruno * In the 8000 HW family the format of the 4 bytes of CSR_HW_REV have 5858303628Ssbruno * changed, and now the revision step also includes bit 0-1 (no more 5859303628Ssbruno * "dash" value). To keep hw_rev backwards compatible - we'll store it 5860303628Ssbruno * in the old format. 5861303628Ssbruno */ 5862330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) 5863303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | 5864303628Ssbruno (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); 5865303628Ssbruno 5866286441Srpaulo if (iwm_prepare_card_hw(sc) != 0) { 5867286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 5868286441Srpaulo goto fail; 5869286441Srpaulo } 5870286441Srpaulo 5871330166Seadler if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { 5872303628Ssbruno int ret; 5873303628Ssbruno uint32_t hw_step; 5874303628Ssbruno 5875303628Ssbruno /* 5876303628Ssbruno * In order to recognize C step the driver should read the 5877303628Ssbruno * chip version id located at the AUX bus MISC address. 5878303628Ssbruno */ 5879303628Ssbruno IWM_SETBITS(sc, IWM_CSR_GP_CNTRL, 5880303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_INIT_DONE); 5881303628Ssbruno DELAY(2); 5882303628Ssbruno 5883303628Ssbruno ret = iwm_poll_bit(sc, IWM_CSR_GP_CNTRL, 5884303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 5885303628Ssbruno IWM_CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, 5886303628Ssbruno 25000); 5887330149Seadler if (!ret) { 5888303628Ssbruno device_printf(sc->sc_dev, 5889303628Ssbruno "Failed to wake up the nic\n"); 5890303628Ssbruno goto fail; 5891303628Ssbruno } 5892303628Ssbruno 5893303628Ssbruno if (iwm_nic_lock(sc)) { 5894303628Ssbruno hw_step = iwm_read_prph(sc, IWM_WFPM_CTRL_REG); 5895303628Ssbruno hw_step |= IWM_ENABLE_WFPM; 5896303628Ssbruno iwm_write_prph(sc, IWM_WFPM_CTRL_REG, hw_step); 5897303628Ssbruno hw_step = iwm_read_prph(sc, IWM_AUX_MISC_REG); 5898303628Ssbruno hw_step = (hw_step >> IWM_HW_STEP_LOCATION_BITS) & 0xF; 5899303628Ssbruno if (hw_step == 0x3) 5900303628Ssbruno sc->sc_hw_rev = (sc->sc_hw_rev & 0xFFFFFFF3) | 5901303628Ssbruno (IWM_SILICON_C_STEP << 2); 5902303628Ssbruno iwm_nic_unlock(sc); 5903303628Ssbruno } else { 5904303628Ssbruno device_printf(sc->sc_dev, "Failed to lock the nic\n"); 5905303628Ssbruno goto fail; 5906303628Ssbruno } 5907303628Ssbruno } 5908303628Ssbruno 5909330166Seadler /* special-case 7265D, it has the same PCI IDs. */ 5910330166Seadler if (sc->cfg == &iwm7265_cfg && 5911330166Seadler (sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK) == IWM_CSR_HW_REV_TYPE_7265D) { 5912330166Seadler sc->cfg = &iwm7265d_cfg; 5913330166Seadler } 5914330166Seadler 5915286441Srpaulo /* Allocate DMA memory for firmware transfers. */ 5916286441Srpaulo if ((error = iwm_alloc_fwmem(sc)) != 0) { 5917286441Srpaulo device_printf(dev, "could not allocate memory for firmware\n"); 5918286441Srpaulo goto fail; 5919286441Srpaulo } 5920286441Srpaulo 5921286441Srpaulo /* Allocate "Keep Warm" page. */ 5922286441Srpaulo if ((error = iwm_alloc_kw(sc)) != 0) { 5923286441Srpaulo device_printf(dev, "could not allocate keep warm page\n"); 5924286441Srpaulo goto fail; 5925286441Srpaulo } 5926286441Srpaulo 5927286441Srpaulo /* We use ICT interrupts */ 5928286441Srpaulo if ((error = iwm_alloc_ict(sc)) != 0) { 5929286441Srpaulo device_printf(dev, "could not allocate ICT table\n"); 5930286441Srpaulo goto fail; 5931286441Srpaulo } 5932286441Srpaulo 5933286441Srpaulo /* Allocate TX scheduler "rings". */ 5934286441Srpaulo if ((error = iwm_alloc_sched(sc)) != 0) { 5935286441Srpaulo device_printf(dev, "could not allocate TX scheduler rings\n"); 5936286441Srpaulo goto fail; 5937286441Srpaulo } 5938286441Srpaulo 5939286441Srpaulo /* Allocate TX rings */ 5940286441Srpaulo for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) { 5941286441Srpaulo if ((error = iwm_alloc_tx_ring(sc, 5942286441Srpaulo &sc->txq[txq_i], txq_i)) != 0) { 5943286441Srpaulo device_printf(dev, 5944286441Srpaulo "could not allocate TX ring %d\n", 5945286441Srpaulo txq_i); 5946286441Srpaulo goto fail; 5947286441Srpaulo } 5948286441Srpaulo } 5949286441Srpaulo 5950286441Srpaulo /* Allocate RX ring. */ 5951286441Srpaulo if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) { 5952286441Srpaulo device_printf(dev, "could not allocate RX ring\n"); 5953286441Srpaulo goto fail; 5954286441Srpaulo } 5955286441Srpaulo 5956286441Srpaulo /* Clear pending interrupts. */ 5957286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); 5958286441Srpaulo 5959286441Srpaulo ic->ic_softc = sc; 5960286441Srpaulo ic->ic_name = device_get_nameunit(sc->sc_dev); 5961286441Srpaulo ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 5962286441Srpaulo ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 5963286441Srpaulo 5964286441Srpaulo /* Set device capabilities. */ 5965286441Srpaulo ic->ic_caps = 5966286441Srpaulo IEEE80211_C_STA | 5967286441Srpaulo IEEE80211_C_WPA | /* WPA/RSN */ 5968286441Srpaulo IEEE80211_C_WME | 5969286441Srpaulo IEEE80211_C_SHSLOT | /* short slot time supported */ 5970286441Srpaulo IEEE80211_C_SHPREAMBLE /* short preamble supported */ 5971286441Srpaulo// IEEE80211_C_BGSCAN /* capable of bg scanning */ 5972286441Srpaulo ; 5973286441Srpaulo for (i = 0; i < nitems(sc->sc_phyctxt); i++) { 5974286441Srpaulo sc->sc_phyctxt[i].id = i; 5975286441Srpaulo sc->sc_phyctxt[i].color = 0; 5976286441Srpaulo sc->sc_phyctxt[i].ref = 0; 5977286441Srpaulo sc->sc_phyctxt[i].channel = NULL; 5978286441Srpaulo } 5979286441Srpaulo 5980330144Seadler /* Default noise floor */ 5981330144Seadler sc->sc_noise = -96; 5982330144Seadler 5983286441Srpaulo /* Max RSSI */ 5984286441Srpaulo sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; 5985330144Seadler 5986286441Srpaulo sc->sc_preinit_hook.ich_func = iwm_preinit; 5987286441Srpaulo sc->sc_preinit_hook.ich_arg = sc; 5988286441Srpaulo if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { 5989286441Srpaulo device_printf(dev, "config_intrhook_establish failed\n"); 5990286441Srpaulo goto fail; 5991286441Srpaulo } 5992286441Srpaulo 5993286441Srpaulo#ifdef IWM_DEBUG 5994286441Srpaulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 5995286441Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", 5996286441Srpaulo CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); 5997286441Srpaulo#endif 5998286441Srpaulo 5999286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6000286441Srpaulo "<-%s\n", __func__); 6001286441Srpaulo 6002286441Srpaulo return 0; 6003286441Srpaulo 6004286441Srpaulo /* Free allocated memory if something failed during attachment. */ 6005286441Srpaulofail: 6006286441Srpaulo iwm_detach_local(sc, 0); 6007286441Srpaulo 6008286441Srpaulo return ENXIO; 6009286441Srpaulo} 6010286441Srpaulo 6011286441Srpaulostatic int 6012303628Ssbrunoiwm_is_valid_ether_addr(uint8_t *addr) 6013303628Ssbruno{ 6014303628Ssbruno char zero_addr[IEEE80211_ADDR_LEN] = { 0, 0, 0, 0, 0, 0 }; 6015303628Ssbruno 6016303628Ssbruno if ((addr[0] & 1) || IEEE80211_ADDR_EQ(zero_addr, addr)) 6017303628Ssbruno return (FALSE); 6018303628Ssbruno 6019303628Ssbruno return (TRUE); 6020303628Ssbruno} 6021303628Ssbruno 6022303628Ssbrunostatic int 6023286441Srpauloiwm_update_edca(struct ieee80211com *ic) 6024286441Srpaulo{ 6025286865Sadrian struct iwm_softc *sc = ic->ic_softc; 6026286441Srpaulo 6027286441Srpaulo device_printf(sc->sc_dev, "%s: called\n", __func__); 6028286441Srpaulo return (0); 6029286441Srpaulo} 6030286441Srpaulo 6031286441Srpaulostatic void 6032286441Srpauloiwm_preinit(void *arg) 6033286441Srpaulo{ 6034286441Srpaulo struct iwm_softc *sc = arg; 6035286441Srpaulo device_t dev = sc->sc_dev; 6036287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 6037286441Srpaulo int error; 6038286441Srpaulo 6039286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6040286441Srpaulo "->%s\n", __func__); 6041286441Srpaulo 6042286441Srpaulo IWM_LOCK(sc); 6043286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 6044286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 6045286441Srpaulo IWM_UNLOCK(sc); 6046286441Srpaulo goto fail; 6047286441Srpaulo } 6048286441Srpaulo 6049286441Srpaulo error = iwm_run_init_mvm_ucode(sc, 1); 6050286441Srpaulo iwm_stop_device(sc); 6051286441Srpaulo if (error) { 6052286441Srpaulo IWM_UNLOCK(sc); 6053286441Srpaulo goto fail; 6054286441Srpaulo } 6055286441Srpaulo device_printf(dev, 6056303628Ssbruno "hw rev 0x%x, fw ver %s, address %s\n", 6057286441Srpaulo sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, 6058330165Seadler sc->sc_fwver, ether_sprintf(sc->nvm_data->hw_addr)); 6059286441Srpaulo 6060286441Srpaulo /* not all hardware can do 5GHz band */ 6061330165Seadler if (!sc->nvm_data->sku_cap_band_52GHz_enable) 6062286441Srpaulo memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, 6063286441Srpaulo sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A])); 6064286441Srpaulo IWM_UNLOCK(sc); 6065286441Srpaulo 6066298877Savos iwm_init_channel_map(ic, IEEE80211_CHAN_MAX, &ic->ic_nchans, 6067298877Savos ic->ic_channels); 6068298877Savos 6069286441Srpaulo /* 6070286441Srpaulo * At this point we've committed - if we fail to do setup, 6071286441Srpaulo * we now also have to tear down the net80211 state. 6072286441Srpaulo */ 6073287197Sglebius ieee80211_ifattach(ic); 6074286441Srpaulo ic->ic_vap_create = iwm_vap_create; 6075286441Srpaulo ic->ic_vap_delete = iwm_vap_delete; 6076286441Srpaulo ic->ic_raw_xmit = iwm_raw_xmit; 6077286441Srpaulo ic->ic_node_alloc = iwm_node_alloc; 6078286441Srpaulo ic->ic_scan_start = iwm_scan_start; 6079286441Srpaulo ic->ic_scan_end = iwm_scan_end; 6080286441Srpaulo ic->ic_update_mcast = iwm_update_mcast; 6081298877Savos ic->ic_getradiocaps = iwm_init_channel_map; 6082286441Srpaulo ic->ic_set_channel = iwm_set_channel; 6083286441Srpaulo ic->ic_scan_curchan = iwm_scan_curchan; 6084286441Srpaulo ic->ic_scan_mindwell = iwm_scan_mindwell; 6085286441Srpaulo ic->ic_wme.wme_update = iwm_update_edca; 6086287197Sglebius ic->ic_parent = iwm_parent; 6087287197Sglebius ic->ic_transmit = iwm_transmit; 6088286441Srpaulo iwm_radiotap_attach(sc); 6089286441Srpaulo if (bootverbose) 6090286441Srpaulo ieee80211_announce(ic); 6091286441Srpaulo 6092286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6093286441Srpaulo "<-%s\n", __func__); 6094286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 6095286441Srpaulo 6096286441Srpaulo return; 6097286441Srpaulofail: 6098286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 6099286441Srpaulo iwm_detach_local(sc, 0); 6100286441Srpaulo} 6101286441Srpaulo 6102286441Srpaulo/* 6103286441Srpaulo * Attach the interface to 802.11 radiotap. 6104286441Srpaulo */ 6105286441Srpaulostatic void 6106286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc) 6107286441Srpaulo{ 6108287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 6109286441Srpaulo 6110286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6111286441Srpaulo "->%s begin\n", __func__); 6112286441Srpaulo ieee80211_radiotap_attach(ic, 6113286441Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 6114286441Srpaulo IWM_TX_RADIOTAP_PRESENT, 6115286441Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 6116286441Srpaulo IWM_RX_RADIOTAP_PRESENT); 6117286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 6118286441Srpaulo "->%s end\n", __func__); 6119286441Srpaulo} 6120286441Srpaulo 6121286441Srpaulostatic struct ieee80211vap * 6122286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 6123286441Srpaulo enum ieee80211_opmode opmode, int flags, 6124286441Srpaulo const uint8_t bssid[IEEE80211_ADDR_LEN], 6125286441Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]) 6126286441Srpaulo{ 6127286441Srpaulo struct iwm_vap *ivp; 6128286441Srpaulo struct ieee80211vap *vap; 6129286441Srpaulo 6130286441Srpaulo if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 6131286441Srpaulo return NULL; 6132287197Sglebius ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO); 6133286441Srpaulo vap = &ivp->iv_vap; 6134287197Sglebius ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); 6135286441Srpaulo vap->iv_bmissthreshold = 10; /* override default */ 6136286441Srpaulo /* Override with driver methods. */ 6137286441Srpaulo ivp->iv_newstate = vap->iv_newstate; 6138286441Srpaulo vap->iv_newstate = iwm_newstate; 6139286441Srpaulo 6140286441Srpaulo ieee80211_ratectl_init(vap); 6141286441Srpaulo /* Complete setup. */ 6142287197Sglebius ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status, 6143287197Sglebius mac); 6144286441Srpaulo ic->ic_opmode = opmode; 6145286441Srpaulo 6146286441Srpaulo return vap; 6147286441Srpaulo} 6148286441Srpaulo 6149286441Srpaulostatic void 6150286441Srpauloiwm_vap_delete(struct ieee80211vap *vap) 6151286441Srpaulo{ 6152286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 6153286441Srpaulo 6154286441Srpaulo ieee80211_ratectl_deinit(vap); 6155286441Srpaulo ieee80211_vap_detach(vap); 6156286441Srpaulo free(ivp, M_80211_VAP); 6157286441Srpaulo} 6158286441Srpaulo 6159286441Srpaulostatic void 6160286441Srpauloiwm_scan_start(struct ieee80211com *ic) 6161286441Srpaulo{ 6162286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6163303628Ssbruno struct iwm_softc *sc = ic->ic_softc; 6164286441Srpaulo int error; 6165286441Srpaulo 6166286441Srpaulo IWM_LOCK(sc); 6167303628Ssbruno if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) 6168303628Ssbruno error = iwm_mvm_umac_scan(sc); 6169303628Ssbruno else 6170303628Ssbruno error = iwm_mvm_lmac_scan(sc); 6171303628Ssbruno if (error != 0) { 6172301190Sadrian device_printf(sc->sc_dev, "could not initiate 2 GHz scan\n"); 6173286441Srpaulo IWM_UNLOCK(sc); 6174286441Srpaulo ieee80211_cancel_scan(vap); 6175301187Sadrian } else { 6176301187Sadrian iwm_led_blink_start(sc); 6177286441Srpaulo IWM_UNLOCK(sc); 6178301187Sadrian } 6179286441Srpaulo} 6180286441Srpaulo 6181286441Srpaulostatic void 6182286441Srpauloiwm_scan_end(struct ieee80211com *ic) 6183286441Srpaulo{ 6184301187Sadrian struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 6185301187Sadrian struct iwm_softc *sc = ic->ic_softc; 6186301187Sadrian 6187301187Sadrian IWM_LOCK(sc); 6188301187Sadrian iwm_led_blink_stop(sc); 6189301187Sadrian if (vap->iv_state == IEEE80211_S_RUN) 6190301187Sadrian iwm_mvm_led_enable(sc); 6191301187Sadrian IWM_UNLOCK(sc); 6192286441Srpaulo} 6193286441Srpaulo 6194286441Srpaulostatic void 6195286441Srpauloiwm_update_mcast(struct ieee80211com *ic) 6196286441Srpaulo{ 6197286441Srpaulo} 6198286441Srpaulo 6199286441Srpaulostatic void 6200286441Srpauloiwm_set_channel(struct ieee80211com *ic) 6201286441Srpaulo{ 6202286441Srpaulo} 6203286441Srpaulo 6204286441Srpaulostatic void 6205286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 6206286441Srpaulo{ 6207286441Srpaulo} 6208286441Srpaulo 6209286441Srpaulostatic void 6210286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss) 6211286441Srpaulo{ 6212286441Srpaulo return; 6213286441Srpaulo} 6214286441Srpaulo 6215286441Srpaulovoid 6216286441Srpauloiwm_init_task(void *arg1) 6217286441Srpaulo{ 6218286441Srpaulo struct iwm_softc *sc = arg1; 6219286441Srpaulo 6220286441Srpaulo IWM_LOCK(sc); 6221286441Srpaulo while (sc->sc_flags & IWM_FLAG_BUSY) 6222286441Srpaulo msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0); 6223286441Srpaulo sc->sc_flags |= IWM_FLAG_BUSY; 6224287197Sglebius iwm_stop(sc); 6225287197Sglebius if (sc->sc_ic.ic_nrunning > 0) 6226286441Srpaulo iwm_init(sc); 6227286441Srpaulo sc->sc_flags &= ~IWM_FLAG_BUSY; 6228286441Srpaulo wakeup(&sc->sc_flags); 6229286441Srpaulo IWM_UNLOCK(sc); 6230286441Srpaulo} 6231286441Srpaulo 6232286441Srpaulostatic int 6233286441Srpauloiwm_resume(device_t dev) 6234286441Srpaulo{ 6235298612Sadrian struct iwm_softc *sc = device_get_softc(dev); 6236298612Sadrian int do_reinit = 0; 6237286441Srpaulo 6238330140Seadler /* 6239330140Seadler * We disable the RETRY_TIMEOUT register (0x41) to keep 6240330140Seadler * PCI Tx retries from interfering with C3 CPU state. 6241330140Seadler */ 6242330140Seadler pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); 6243286441Srpaulo iwm_init_task(device_get_softc(dev)); 6244286441Srpaulo 6245298612Sadrian IWM_LOCK(sc); 6246303628Ssbruno if (sc->sc_flags & IWM_FLAG_SCANNING) { 6247303628Ssbruno sc->sc_flags &= ~IWM_FLAG_SCANNING; 6248298612Sadrian do_reinit = 1; 6249298612Sadrian } 6250298612Sadrian IWM_UNLOCK(sc); 6251298612Sadrian 6252298612Sadrian if (do_reinit) 6253298612Sadrian ieee80211_resume_all(&sc->sc_ic); 6254298612Sadrian 6255286441Srpaulo return 0; 6256286441Srpaulo} 6257286441Srpaulo 6258286441Srpaulostatic int 6259286441Srpauloiwm_suspend(device_t dev) 6260286441Srpaulo{ 6261298612Sadrian int do_stop = 0; 6262286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6263286441Srpaulo 6264298612Sadrian do_stop = !! (sc->sc_ic.ic_nrunning > 0); 6265298612Sadrian 6266298612Sadrian ieee80211_suspend_all(&sc->sc_ic); 6267298612Sadrian 6268298612Sadrian if (do_stop) { 6269287197Sglebius IWM_LOCK(sc); 6270287197Sglebius iwm_stop(sc); 6271303628Ssbruno sc->sc_flags |= IWM_FLAG_SCANNING; 6272287197Sglebius IWM_UNLOCK(sc); 6273287197Sglebius } 6274286441Srpaulo 6275286441Srpaulo return (0); 6276286441Srpaulo} 6277286441Srpaulo 6278286441Srpaulostatic int 6279286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211) 6280286441Srpaulo{ 6281293219Savos struct iwm_fw_info *fw = &sc->sc_fw; 6282286441Srpaulo device_t dev = sc->sc_dev; 6283286441Srpaulo int i; 6284286441Srpaulo 6285330164Seadler if (!sc->sc_attached) 6286330164Seadler return 0; 6287330164Seadler sc->sc_attached = 0; 6288330164Seadler 6289330159Seadler if (do_net80211) 6290330159Seadler ieee80211_draintask(&sc->sc_ic, &sc->sc_es_task); 6291303628Ssbruno 6292301187Sadrian callout_drain(&sc->sc_led_blink_to); 6293287197Sglebius callout_drain(&sc->sc_watchdog_to); 6294287197Sglebius iwm_stop_device(sc); 6295303628Ssbruno if (do_net80211) { 6296287197Sglebius ieee80211_ifdetach(&sc->sc_ic); 6297303628Ssbruno } 6298286441Srpaulo 6299330163Seadler iwm_phy_db_free(sc->sc_phy_db); 6300330163Seadler sc->sc_phy_db = NULL; 6301302103Sadrian 6302330165Seadler iwm_free_nvm_data(sc->nvm_data); 6303330165Seadler 6304286441Srpaulo /* Free descriptor rings */ 6305301970Sadrian iwm_free_rx_ring(sc, &sc->rxq); 6306286441Srpaulo for (i = 0; i < nitems(sc->txq); i++) 6307286441Srpaulo iwm_free_tx_ring(sc, &sc->txq[i]); 6308286441Srpaulo 6309293219Savos /* Free firmware */ 6310293219Savos if (fw->fw_fp != NULL) 6311293219Savos iwm_fw_info_free(fw); 6312293219Savos 6313293178Savos /* Free scheduler */ 6314330150Seadler iwm_dma_contig_free(&sc->sched_dma); 6315330150Seadler iwm_dma_contig_free(&sc->ict_dma); 6316330150Seadler iwm_dma_contig_free(&sc->kw_dma); 6317330150Seadler iwm_dma_contig_free(&sc->fw_dma); 6318286441Srpaulo 6319286441Srpaulo /* Finished with the hardware - detach things */ 6320286441Srpaulo iwm_pci_detach(dev); 6321286441Srpaulo 6322287197Sglebius mbufq_drain(&sc->sc_snd); 6323293099Savos IWM_LOCK_DESTROY(sc); 6324286441Srpaulo 6325286441Srpaulo return (0); 6326286441Srpaulo} 6327286441Srpaulo 6328286441Srpaulostatic int 6329286441Srpauloiwm_detach(device_t dev) 6330286441Srpaulo{ 6331286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 6332286441Srpaulo 6333286441Srpaulo return (iwm_detach_local(sc, 1)); 6334286441Srpaulo} 6335286441Srpaulo 6336286441Srpaulostatic device_method_t iwm_pci_methods[] = { 6337286441Srpaulo /* Device interface */ 6338286441Srpaulo DEVMETHOD(device_probe, iwm_probe), 6339286441Srpaulo DEVMETHOD(device_attach, iwm_attach), 6340286441Srpaulo DEVMETHOD(device_detach, iwm_detach), 6341286441Srpaulo DEVMETHOD(device_suspend, iwm_suspend), 6342286441Srpaulo DEVMETHOD(device_resume, iwm_resume), 6343286441Srpaulo 6344286441Srpaulo DEVMETHOD_END 6345286441Srpaulo}; 6346286441Srpaulo 6347286441Srpaulostatic driver_t iwm_pci_driver = { 6348286441Srpaulo "iwm", 6349286441Srpaulo iwm_pci_methods, 6350286441Srpaulo sizeof (struct iwm_softc) 6351286441Srpaulo}; 6352286441Srpaulo 6353286441Srpaulostatic devclass_t iwm_devclass; 6354286441Srpaulo 6355286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL); 6356286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1); 6357286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1); 6358286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1); 6359