if_iwm.c revision 287965
1286441Srpaulo/* $OpenBSD: if_iwm.c,v 1.39 2015/03/23 00:35:19 jsg 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: head/sys/dev/iwm/if_iwm.c 287965 2015-09-18 17:39:31Z adrian $"); 107286441Srpaulo 108286441Srpaulo#include <sys/param.h> 109286441Srpaulo#include <sys/bus.h> 110286441Srpaulo#include <sys/conf.h> 111286441Srpaulo#include <sys/endian.h> 112286441Srpaulo#include <sys/firmware.h> 113286441Srpaulo#include <sys/kernel.h> 114286441Srpaulo#include <sys/malloc.h> 115286441Srpaulo#include <sys/mbuf.h> 116286441Srpaulo#include <sys/mutex.h> 117286441Srpaulo#include <sys/module.h> 118286441Srpaulo#include <sys/proc.h> 119286441Srpaulo#include <sys/rman.h> 120286441Srpaulo#include <sys/socket.h> 121286441Srpaulo#include <sys/sockio.h> 122286441Srpaulo#include <sys/sysctl.h> 123286441Srpaulo#include <sys/linker.h> 124286441Srpaulo 125286441Srpaulo#include <machine/bus.h> 126286441Srpaulo#include <machine/endian.h> 127286441Srpaulo#include <machine/resource.h> 128286441Srpaulo 129286441Srpaulo#include <dev/pci/pcivar.h> 130286441Srpaulo#include <dev/pci/pcireg.h> 131286441Srpaulo 132286441Srpaulo#include <net/bpf.h> 133286441Srpaulo 134286441Srpaulo#include <net/if.h> 135286441Srpaulo#include <net/if_var.h> 136286441Srpaulo#include <net/if_arp.h> 137286441Srpaulo#include <net/if_dl.h> 138286441Srpaulo#include <net/if_media.h> 139286441Srpaulo#include <net/if_types.h> 140286441Srpaulo 141286441Srpaulo#include <netinet/in.h> 142286441Srpaulo#include <netinet/in_systm.h> 143286441Srpaulo#include <netinet/if_ether.h> 144286441Srpaulo#include <netinet/ip.h> 145286441Srpaulo 146286441Srpaulo#include <net80211/ieee80211_var.h> 147286441Srpaulo#include <net80211/ieee80211_regdomain.h> 148286441Srpaulo#include <net80211/ieee80211_ratectl.h> 149286441Srpaulo#include <net80211/ieee80211_radiotap.h> 150286441Srpaulo 151286475Srpaulo#include <dev/iwm/if_iwmreg.h> 152286475Srpaulo#include <dev/iwm/if_iwmvar.h> 153286475Srpaulo#include <dev/iwm/if_iwm_debug.h> 154286475Srpaulo#include <dev/iwm/if_iwm_util.h> 155286475Srpaulo#include <dev/iwm/if_iwm_binding.h> 156286475Srpaulo#include <dev/iwm/if_iwm_phy_db.h> 157286475Srpaulo#include <dev/iwm/if_iwm_mac_ctxt.h> 158286475Srpaulo#include <dev/iwm/if_iwm_phy_ctxt.h> 159286475Srpaulo#include <dev/iwm/if_iwm_time_event.h> 160286475Srpaulo#include <dev/iwm/if_iwm_power.h> 161286475Srpaulo#include <dev/iwm/if_iwm_scan.h> 162286441Srpaulo 163286475Srpaulo#include <dev/iwm/if_iwm_pcie_trans.h> 164286441Srpaulo 165286441Srpauloconst uint8_t iwm_nvm_channels[] = { 166286441Srpaulo /* 2.4 GHz */ 167286441Srpaulo 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 168286441Srpaulo /* 5 GHz */ 169286441Srpaulo 36, 40, 44 , 48, 52, 56, 60, 64, 170286441Srpaulo 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144, 171286441Srpaulo 149, 153, 157, 161, 165 172286441Srpaulo}; 173286441Srpaulo#define IWM_NUM_2GHZ_CHANNELS 14 174286441Srpaulo 175286441Srpaulo/* 176286441Srpaulo * XXX For now, there's simply a fixed set of rate table entries 177286441Srpaulo * that are populated. 178286441Srpaulo */ 179286441Srpauloconst struct iwm_rate { 180286441Srpaulo uint8_t rate; 181286441Srpaulo uint8_t plcp; 182286441Srpaulo} iwm_rates[] = { 183286441Srpaulo { 2, IWM_RATE_1M_PLCP }, 184286441Srpaulo { 4, IWM_RATE_2M_PLCP }, 185286441Srpaulo { 11, IWM_RATE_5M_PLCP }, 186286441Srpaulo { 22, IWM_RATE_11M_PLCP }, 187286441Srpaulo { 12, IWM_RATE_6M_PLCP }, 188286441Srpaulo { 18, IWM_RATE_9M_PLCP }, 189286441Srpaulo { 24, IWM_RATE_12M_PLCP }, 190286441Srpaulo { 36, IWM_RATE_18M_PLCP }, 191286441Srpaulo { 48, IWM_RATE_24M_PLCP }, 192286441Srpaulo { 72, IWM_RATE_36M_PLCP }, 193286441Srpaulo { 96, IWM_RATE_48M_PLCP }, 194286441Srpaulo { 108, IWM_RATE_54M_PLCP }, 195286441Srpaulo}; 196286441Srpaulo#define IWM_RIDX_CCK 0 197286441Srpaulo#define IWM_RIDX_OFDM 4 198286441Srpaulo#define IWM_RIDX_MAX (nitems(iwm_rates)-1) 199286441Srpaulo#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM) 200286441Srpaulo#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM) 201286441Srpaulo 202286441Srpaulostatic int iwm_store_cscheme(struct iwm_softc *, const uint8_t *, size_t); 203286441Srpaulostatic int iwm_firmware_store_section(struct iwm_softc *, 204286441Srpaulo enum iwm_ucode_type, 205286441Srpaulo const uint8_t *, size_t); 206286441Srpaulostatic int iwm_set_default_calib(struct iwm_softc *, const void *); 207286441Srpaulostatic void iwm_fw_info_free(struct iwm_fw_info *); 208286441Srpaulostatic int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); 209286441Srpaulostatic void iwm_dma_map_addr(void *, bus_dma_segment_t *, int, int); 210286441Srpaulostatic int iwm_dma_contig_alloc(bus_dma_tag_t, struct iwm_dma_info *, 211286441Srpaulo bus_size_t, bus_size_t); 212286441Srpaulostatic void iwm_dma_contig_free(struct iwm_dma_info *); 213286441Srpaulostatic int iwm_alloc_fwmem(struct iwm_softc *); 214286441Srpaulostatic void iwm_free_fwmem(struct iwm_softc *); 215286441Srpaulostatic int iwm_alloc_sched(struct iwm_softc *); 216286441Srpaulostatic void iwm_free_sched(struct iwm_softc *); 217286441Srpaulostatic int iwm_alloc_kw(struct iwm_softc *); 218286441Srpaulostatic void iwm_free_kw(struct iwm_softc *); 219286441Srpaulostatic int iwm_alloc_ict(struct iwm_softc *); 220286441Srpaulostatic void iwm_free_ict(struct iwm_softc *); 221286441Srpaulostatic int iwm_alloc_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 222286441Srpaulostatic void iwm_reset_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 223286441Srpaulostatic void iwm_free_rx_ring(struct iwm_softc *, struct iwm_rx_ring *); 224286441Srpaulostatic int iwm_alloc_tx_ring(struct iwm_softc *, struct iwm_tx_ring *, 225286441Srpaulo int); 226286441Srpaulostatic void iwm_reset_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 227286441Srpaulostatic void iwm_free_tx_ring(struct iwm_softc *, struct iwm_tx_ring *); 228286441Srpaulostatic void iwm_enable_interrupts(struct iwm_softc *); 229286441Srpaulostatic void iwm_restore_interrupts(struct iwm_softc *); 230286441Srpaulostatic void iwm_disable_interrupts(struct iwm_softc *); 231286441Srpaulostatic void iwm_ict_reset(struct iwm_softc *); 232286441Srpaulostatic int iwm_allow_mcast(struct ieee80211vap *, struct iwm_softc *); 233286441Srpaulostatic void iwm_stop_device(struct iwm_softc *); 234286441Srpaulostatic void iwm_mvm_nic_config(struct iwm_softc *); 235286441Srpaulostatic int iwm_nic_rx_init(struct iwm_softc *); 236286441Srpaulostatic int iwm_nic_tx_init(struct iwm_softc *); 237286441Srpaulostatic int iwm_nic_init(struct iwm_softc *); 238286441Srpaulostatic void iwm_enable_txq(struct iwm_softc *, int, int); 239286441Srpaulostatic int iwm_post_alive(struct iwm_softc *); 240286441Srpaulostatic int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, 241286441Srpaulo uint16_t, uint8_t *, uint16_t *); 242286441Srpaulostatic int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, 243286441Srpaulo uint16_t *); 244286441Srpaulostatic void iwm_init_channel_map(struct iwm_softc *, 245286441Srpaulo const uint16_t * const); 246286441Srpaulostatic int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, 247286441Srpaulo const uint16_t *, const uint16_t *, uint8_t, 248286441Srpaulo uint8_t); 249286441Srpaulostruct iwm_nvm_section; 250286441Srpaulostatic int iwm_parse_nvm_sections(struct iwm_softc *, 251286441Srpaulo struct iwm_nvm_section *); 252286441Srpaulostatic int iwm_nvm_init(struct iwm_softc *); 253286441Srpaulostatic int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, 254286441Srpaulo const uint8_t *, uint32_t); 255286441Srpaulostatic int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); 256286441Srpaulostatic int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); 257286441Srpaulostatic int iwm_fw_alive(struct iwm_softc *, uint32_t); 258286441Srpaulostatic int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); 259286441Srpaulostatic int iwm_send_phy_cfg_cmd(struct iwm_softc *); 260286441Srpaulostatic int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, 261286441Srpaulo enum iwm_ucode_type); 262286441Srpaulostatic int iwm_run_init_mvm_ucode(struct iwm_softc *, int); 263286441Srpaulostatic int iwm_rx_addbuf(struct iwm_softc *, int, int); 264286441Srpaulostatic int iwm_mvm_calc_rssi(struct iwm_softc *, struct iwm_rx_phy_info *); 265286441Srpaulostatic int iwm_mvm_get_signal_strength(struct iwm_softc *, 266286441Srpaulo struct iwm_rx_phy_info *); 267286441Srpaulostatic void iwm_mvm_rx_rx_phy_cmd(struct iwm_softc *, 268286441Srpaulo struct iwm_rx_packet *, 269286441Srpaulo struct iwm_rx_data *); 270286441Srpaulostatic int iwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *); 271286441Srpaulostatic void iwm_mvm_rx_rx_mpdu(struct iwm_softc *, struct iwm_rx_packet *, 272286441Srpaulo struct iwm_rx_data *); 273286441Srpaulostatic void iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, 274286441Srpaulo struct iwm_rx_packet *, 275286441Srpaulo struct iwm_node *); 276286441Srpaulostatic void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, 277286441Srpaulo struct iwm_rx_data *); 278286441Srpaulostatic void iwm_cmd_done(struct iwm_softc *, struct iwm_rx_packet *); 279286441Srpaulo#if 0 280286441Srpaulostatic void iwm_update_sched(struct iwm_softc *, int, int, uint8_t, 281286441Srpaulo uint16_t); 282286441Srpaulo#endif 283286441Srpaulostatic const struct iwm_rate * 284286441Srpaulo iwm_tx_fill_cmd(struct iwm_softc *, struct iwm_node *, 285286441Srpaulo struct ieee80211_frame *, struct iwm_tx_cmd *); 286286441Srpaulostatic int iwm_tx(struct iwm_softc *, struct mbuf *, 287286441Srpaulo struct ieee80211_node *, int); 288286441Srpaulostatic int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, 289286441Srpaulo const struct ieee80211_bpf_params *); 290286441Srpaulostatic void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, 291286441Srpaulo struct iwm_mvm_add_sta_cmd_v5 *); 292286441Srpaulostatic int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, 293286441Srpaulo struct iwm_mvm_add_sta_cmd_v6 *, 294286441Srpaulo int *); 295286441Srpaulostatic int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, 296286441Srpaulo int); 297286441Srpaulostatic int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); 298286441Srpaulostatic int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); 299286441Srpaulostatic int iwm_mvm_add_int_sta_common(struct iwm_softc *, 300286441Srpaulo struct iwm_int_sta *, 301286441Srpaulo const uint8_t *, uint16_t, uint16_t); 302286441Srpaulostatic int iwm_mvm_add_aux_sta(struct iwm_softc *); 303286441Srpaulostatic int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_node *); 304286441Srpaulostatic int iwm_auth(struct ieee80211vap *, struct iwm_softc *); 305286441Srpaulostatic int iwm_assoc(struct ieee80211vap *, struct iwm_softc *); 306286441Srpaulostatic int iwm_release(struct iwm_softc *, struct iwm_node *); 307286441Srpaulostatic struct ieee80211_node * 308286441Srpaulo iwm_node_alloc(struct ieee80211vap *, 309286441Srpaulo const uint8_t[IEEE80211_ADDR_LEN]); 310286441Srpaulostatic void iwm_setrates(struct iwm_softc *, struct iwm_node *); 311286441Srpaulostatic int iwm_media_change(struct ifnet *); 312286441Srpaulostatic int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); 313286441Srpaulostatic void iwm_endscan_cb(void *, int); 314286441Srpaulostatic int iwm_init_hw(struct iwm_softc *); 315287197Sglebiusstatic void iwm_init(struct iwm_softc *); 316287197Sglebiusstatic void iwm_start(struct iwm_softc *); 317287197Sglebiusstatic void iwm_stop(struct iwm_softc *); 318286441Srpaulostatic void iwm_watchdog(void *); 319287197Sglebiusstatic void iwm_parent(struct ieee80211com *); 320286441Srpaulo#ifdef IWM_DEBUG 321286441Srpaulostatic const char * 322286441Srpaulo iwm_desc_lookup(uint32_t); 323286441Srpaulostatic void iwm_nic_error(struct iwm_softc *); 324286441Srpaulo#endif 325286441Srpaulostatic void iwm_notif_intr(struct iwm_softc *); 326286441Srpaulostatic void iwm_intr(void *); 327286441Srpaulostatic int iwm_attach(device_t); 328286441Srpaulostatic void iwm_preinit(void *); 329286441Srpaulostatic int iwm_detach_local(struct iwm_softc *sc, int); 330286441Srpaulostatic void iwm_init_task(void *); 331286441Srpaulostatic void iwm_radiotap_attach(struct iwm_softc *); 332286441Srpaulostatic struct ieee80211vap * 333286441Srpaulo iwm_vap_create(struct ieee80211com *, 334286441Srpaulo const char [IFNAMSIZ], int, 335286441Srpaulo enum ieee80211_opmode, int, 336286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN], 337286441Srpaulo const uint8_t [IEEE80211_ADDR_LEN]); 338286441Srpaulostatic void iwm_vap_delete(struct ieee80211vap *); 339286441Srpaulostatic void iwm_scan_start(struct ieee80211com *); 340286441Srpaulostatic void iwm_scan_end(struct ieee80211com *); 341286441Srpaulostatic void iwm_update_mcast(struct ieee80211com *); 342286441Srpaulostatic void iwm_set_channel(struct ieee80211com *); 343286441Srpaulostatic void iwm_scan_curchan(struct ieee80211_scan_state *, unsigned long); 344286441Srpaulostatic void iwm_scan_mindwell(struct ieee80211_scan_state *); 345286441Srpaulostatic int iwm_detach(device_t); 346286441Srpaulo 347286441Srpaulo/* 348286441Srpaulo * Firmware parser. 349286441Srpaulo */ 350286441Srpaulo 351286441Srpaulostatic int 352286441Srpauloiwm_store_cscheme(struct iwm_softc *sc, const uint8_t *data, size_t dlen) 353286441Srpaulo{ 354286441Srpaulo const struct iwm_fw_cscheme_list *l = (const void *)data; 355286441Srpaulo 356286441Srpaulo if (dlen < sizeof(*l) || 357286441Srpaulo dlen < sizeof(l->size) + l->size * sizeof(*l->cs)) 358286441Srpaulo return EINVAL; 359286441Srpaulo 360286441Srpaulo /* we don't actually store anything for now, always use s/w crypto */ 361286441Srpaulo 362286441Srpaulo return 0; 363286441Srpaulo} 364286441Srpaulo 365286441Srpaulostatic int 366286441Srpauloiwm_firmware_store_section(struct iwm_softc *sc, 367286441Srpaulo enum iwm_ucode_type type, const uint8_t *data, size_t dlen) 368286441Srpaulo{ 369286441Srpaulo struct iwm_fw_sects *fws; 370286441Srpaulo struct iwm_fw_onesect *fwone; 371286441Srpaulo 372286441Srpaulo if (type >= IWM_UCODE_TYPE_MAX) 373286441Srpaulo return EINVAL; 374286441Srpaulo if (dlen < sizeof(uint32_t)) 375286441Srpaulo return EINVAL; 376286441Srpaulo 377286441Srpaulo fws = &sc->sc_fw.fw_sects[type]; 378286441Srpaulo if (fws->fw_count >= IWM_UCODE_SECT_MAX) 379286441Srpaulo return EINVAL; 380286441Srpaulo 381286441Srpaulo fwone = &fws->fw_sect[fws->fw_count]; 382286441Srpaulo 383286441Srpaulo /* first 32bit are device load offset */ 384286441Srpaulo memcpy(&fwone->fws_devoff, data, sizeof(uint32_t)); 385286441Srpaulo 386286441Srpaulo /* rest is data */ 387286441Srpaulo fwone->fws_data = data + sizeof(uint32_t); 388286441Srpaulo fwone->fws_len = dlen - sizeof(uint32_t); 389286441Srpaulo 390286441Srpaulo fws->fw_count++; 391286441Srpaulo fws->fw_totlen += fwone->fws_len; 392286441Srpaulo 393286441Srpaulo return 0; 394286441Srpaulo} 395286441Srpaulo 396286441Srpaulo/* iwlwifi: iwl-drv.c */ 397286441Srpaulostruct iwm_tlv_calib_data { 398286441Srpaulo uint32_t ucode_type; 399286441Srpaulo struct iwm_tlv_calib_ctrl calib; 400286441Srpaulo} __packed; 401286441Srpaulo 402286441Srpaulostatic int 403286441Srpauloiwm_set_default_calib(struct iwm_softc *sc, const void *data) 404286441Srpaulo{ 405286441Srpaulo const struct iwm_tlv_calib_data *def_calib = data; 406286441Srpaulo uint32_t ucode_type = le32toh(def_calib->ucode_type); 407286441Srpaulo 408286441Srpaulo if (ucode_type >= IWM_UCODE_TYPE_MAX) { 409286441Srpaulo device_printf(sc->sc_dev, 410286441Srpaulo "Wrong ucode_type %u for default " 411286441Srpaulo "calibration.\n", ucode_type); 412286441Srpaulo return EINVAL; 413286441Srpaulo } 414286441Srpaulo 415286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger = 416286441Srpaulo def_calib->calib.flow_trigger; 417286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger = 418286441Srpaulo def_calib->calib.event_trigger; 419286441Srpaulo 420286441Srpaulo return 0; 421286441Srpaulo} 422286441Srpaulo 423286441Srpaulostatic void 424286441Srpauloiwm_fw_info_free(struct iwm_fw_info *fw) 425286441Srpaulo{ 426286441Srpaulo firmware_put(fw->fw_rawdata, FIRMWARE_UNLOAD); 427286441Srpaulo fw->fw_rawdata = NULL; 428286441Srpaulo fw->fw_rawsize = 0; 429286441Srpaulo /* don't touch fw->fw_status */ 430286441Srpaulo memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); 431286441Srpaulo} 432286441Srpaulo 433286441Srpaulostatic int 434286441Srpauloiwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 435286441Srpaulo{ 436286441Srpaulo struct iwm_fw_info *fw = &sc->sc_fw; 437286441Srpaulo const struct iwm_tlv_ucode_header *uhdr; 438286441Srpaulo struct iwm_ucode_tlv tlv; 439286441Srpaulo enum iwm_ucode_tlv_type tlv_type; 440286441Srpaulo const struct firmware *fwp; 441286441Srpaulo const uint8_t *data; 442286441Srpaulo int error = 0; 443286441Srpaulo size_t len; 444286441Srpaulo 445286441Srpaulo if (fw->fw_status == IWM_FW_STATUS_DONE && 446286441Srpaulo ucode_type != IWM_UCODE_TYPE_INIT) 447286441Srpaulo return 0; 448286441Srpaulo 449286441Srpaulo while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) 450286441Srpaulo msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); 451286441Srpaulo fw->fw_status = IWM_FW_STATUS_INPROGRESS; 452286441Srpaulo 453286441Srpaulo if (fw->fw_rawdata != NULL) 454286441Srpaulo iwm_fw_info_free(fw); 455286441Srpaulo 456286441Srpaulo /* 457286441Srpaulo * Load firmware into driver memory. 458286441Srpaulo * fw_rawdata and fw_rawsize will be set. 459286441Srpaulo */ 460286441Srpaulo IWM_UNLOCK(sc); 461286441Srpaulo fwp = firmware_get(sc->sc_fwname); 462286441Srpaulo if (fwp == NULL) { 463286441Srpaulo device_printf(sc->sc_dev, 464286441Srpaulo "could not read firmware %s (error %d)\n", 465286441Srpaulo sc->sc_fwname, error); 466286441Srpaulo IWM_LOCK(sc); 467286441Srpaulo goto out; 468286441Srpaulo } 469286441Srpaulo IWM_LOCK(sc); 470286441Srpaulo fw->fw_rawdata = fwp->data; 471286441Srpaulo fw->fw_rawsize = fwp->datasize; 472286441Srpaulo 473286441Srpaulo /* 474286441Srpaulo * Parse firmware contents 475286441Srpaulo */ 476286441Srpaulo 477286441Srpaulo uhdr = (const void *)fw->fw_rawdata; 478286441Srpaulo if (*(const uint32_t *)fw->fw_rawdata != 0 479286441Srpaulo || le32toh(uhdr->magic) != IWM_TLV_UCODE_MAGIC) { 480286441Srpaulo device_printf(sc->sc_dev, "invalid firmware %s\n", 481286441Srpaulo sc->sc_fwname); 482286441Srpaulo error = EINVAL; 483286441Srpaulo goto out; 484286441Srpaulo } 485286441Srpaulo 486286441Srpaulo sc->sc_fwver = le32toh(uhdr->ver); 487286441Srpaulo data = uhdr->data; 488286441Srpaulo len = fw->fw_rawsize - sizeof(*uhdr); 489286441Srpaulo 490286441Srpaulo while (len >= sizeof(tlv)) { 491286441Srpaulo size_t tlv_len; 492286441Srpaulo const void *tlv_data; 493286441Srpaulo 494286441Srpaulo memcpy(&tlv, data, sizeof(tlv)); 495286441Srpaulo tlv_len = le32toh(tlv.length); 496286441Srpaulo tlv_type = le32toh(tlv.type); 497286441Srpaulo 498286441Srpaulo len -= sizeof(tlv); 499286441Srpaulo data += sizeof(tlv); 500286441Srpaulo tlv_data = data; 501286441Srpaulo 502286441Srpaulo if (len < tlv_len) { 503286441Srpaulo device_printf(sc->sc_dev, 504286441Srpaulo "firmware too short: %zu bytes\n", 505286441Srpaulo len); 506286441Srpaulo error = EINVAL; 507286441Srpaulo goto parse_out; 508286441Srpaulo } 509286441Srpaulo 510286441Srpaulo switch ((int)tlv_type) { 511286441Srpaulo case IWM_UCODE_TLV_PROBE_MAX_LEN: 512286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 513286441Srpaulo device_printf(sc->sc_dev, 514286441Srpaulo "%s: PROBE_MAX_LEN (%d) < sizeof(uint32_t)\n", 515286441Srpaulo __func__, 516286441Srpaulo (int) tlv_len); 517286441Srpaulo error = EINVAL; 518286441Srpaulo goto parse_out; 519286441Srpaulo } 520286441Srpaulo sc->sc_capa_max_probe_len 521286441Srpaulo = le32toh(*(const uint32_t *)tlv_data); 522286441Srpaulo /* limit it to something sensible */ 523286441Srpaulo if (sc->sc_capa_max_probe_len > (1<<16)) { 524286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 525286441Srpaulo "%s: IWM_UCODE_TLV_PROBE_MAX_LEN " 526286441Srpaulo "ridiculous\n", __func__); 527286441Srpaulo error = EINVAL; 528286441Srpaulo goto parse_out; 529286441Srpaulo } 530286441Srpaulo break; 531286441Srpaulo case IWM_UCODE_TLV_PAN: 532286441Srpaulo if (tlv_len) { 533286441Srpaulo device_printf(sc->sc_dev, 534286441Srpaulo "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", 535286441Srpaulo __func__, 536286441Srpaulo (int) tlv_len); 537286441Srpaulo error = EINVAL; 538286441Srpaulo goto parse_out; 539286441Srpaulo } 540286441Srpaulo sc->sc_capaflags |= IWM_UCODE_TLV_FLAGS_PAN; 541286441Srpaulo break; 542286441Srpaulo case IWM_UCODE_TLV_FLAGS: 543286441Srpaulo if (tlv_len < sizeof(uint32_t)) { 544286441Srpaulo device_printf(sc->sc_dev, 545286441Srpaulo "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", 546286441Srpaulo __func__, 547286441Srpaulo (int) tlv_len); 548286441Srpaulo error = EINVAL; 549286441Srpaulo goto parse_out; 550286441Srpaulo } 551286441Srpaulo /* 552286441Srpaulo * Apparently there can be many flags, but Linux driver 553286441Srpaulo * parses only the first one, and so do we. 554286441Srpaulo * 555286441Srpaulo * XXX: why does this override IWM_UCODE_TLV_PAN? 556286441Srpaulo * Intentional or a bug? Observations from 557286441Srpaulo * current firmware file: 558286441Srpaulo * 1) TLV_PAN is parsed first 559286441Srpaulo * 2) TLV_FLAGS contains TLV_FLAGS_PAN 560286441Srpaulo * ==> this resets TLV_PAN to itself... hnnnk 561286441Srpaulo */ 562286441Srpaulo sc->sc_capaflags = le32toh(*(const uint32_t *)tlv_data); 563286441Srpaulo break; 564286441Srpaulo case IWM_UCODE_TLV_CSCHEME: 565286441Srpaulo if ((error = iwm_store_cscheme(sc, 566286441Srpaulo tlv_data, tlv_len)) != 0) { 567286441Srpaulo device_printf(sc->sc_dev, 568286441Srpaulo "%s: iwm_store_cscheme(): returned %d\n", 569286441Srpaulo __func__, 570286441Srpaulo error); 571286441Srpaulo goto parse_out; 572286441Srpaulo } 573286441Srpaulo break; 574286441Srpaulo case IWM_UCODE_TLV_NUM_OF_CPU: 575286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 576286441Srpaulo device_printf(sc->sc_dev, 577286441Srpaulo "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) < sizeof(uint32_t)\n", 578286441Srpaulo __func__, 579286441Srpaulo (int) tlv_len); 580286441Srpaulo error = EINVAL; 581286441Srpaulo goto parse_out; 582286441Srpaulo } 583286441Srpaulo if (le32toh(*(const uint32_t*)tlv_data) != 1) { 584286441Srpaulo device_printf(sc->sc_dev, 585286441Srpaulo "%s: driver supports " 586286441Srpaulo "only TLV_NUM_OF_CPU == 1", 587286441Srpaulo __func__); 588286441Srpaulo error = EINVAL; 589286441Srpaulo goto parse_out; 590286441Srpaulo } 591286441Srpaulo break; 592286441Srpaulo case IWM_UCODE_TLV_SEC_RT: 593286441Srpaulo if ((error = iwm_firmware_store_section(sc, 594286441Srpaulo IWM_UCODE_TYPE_REGULAR, tlv_data, tlv_len)) != 0) { 595286441Srpaulo device_printf(sc->sc_dev, 596286441Srpaulo "%s: IWM_UCODE_TYPE_REGULAR: iwm_firmware_store_section() failed; %d\n", 597286441Srpaulo __func__, 598286441Srpaulo error); 599286441Srpaulo goto parse_out; 600286441Srpaulo } 601286441Srpaulo break; 602286441Srpaulo case IWM_UCODE_TLV_SEC_INIT: 603286441Srpaulo if ((error = iwm_firmware_store_section(sc, 604286441Srpaulo IWM_UCODE_TYPE_INIT, tlv_data, tlv_len)) != 0) { 605286441Srpaulo device_printf(sc->sc_dev, 606286441Srpaulo "%s: IWM_UCODE_TYPE_INIT: iwm_firmware_store_section() failed; %d\n", 607286441Srpaulo __func__, 608286441Srpaulo error); 609286441Srpaulo goto parse_out; 610286441Srpaulo } 611286441Srpaulo break; 612286441Srpaulo case IWM_UCODE_TLV_SEC_WOWLAN: 613286441Srpaulo if ((error = iwm_firmware_store_section(sc, 614286441Srpaulo IWM_UCODE_TYPE_WOW, tlv_data, tlv_len)) != 0) { 615286441Srpaulo device_printf(sc->sc_dev, 616286441Srpaulo "%s: IWM_UCODE_TYPE_WOW: iwm_firmware_store_section() failed; %d\n", 617286441Srpaulo __func__, 618286441Srpaulo error); 619286441Srpaulo goto parse_out; 620286441Srpaulo } 621286441Srpaulo break; 622286441Srpaulo case IWM_UCODE_TLV_DEF_CALIB: 623286441Srpaulo if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { 624286441Srpaulo device_printf(sc->sc_dev, 625286441Srpaulo "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", 626286441Srpaulo __func__, 627286441Srpaulo (int) tlv_len, 628286441Srpaulo (int) sizeof(struct iwm_tlv_calib_data)); 629286441Srpaulo error = EINVAL; 630286441Srpaulo goto parse_out; 631286441Srpaulo } 632286441Srpaulo if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { 633286441Srpaulo device_printf(sc->sc_dev, 634286441Srpaulo "%s: iwm_set_default_calib() failed: %d\n", 635286441Srpaulo __func__, 636286441Srpaulo error); 637286441Srpaulo goto parse_out; 638286441Srpaulo } 639286441Srpaulo break; 640286441Srpaulo case IWM_UCODE_TLV_PHY_SKU: 641286441Srpaulo if (tlv_len != sizeof(uint32_t)) { 642286441Srpaulo error = EINVAL; 643286441Srpaulo device_printf(sc->sc_dev, 644286441Srpaulo "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", 645286441Srpaulo __func__, 646286441Srpaulo (int) tlv_len); 647286441Srpaulo goto parse_out; 648286441Srpaulo } 649286441Srpaulo sc->sc_fw_phy_config = 650286441Srpaulo le32toh(*(const uint32_t *)tlv_data); 651286441Srpaulo break; 652286441Srpaulo 653286441Srpaulo case IWM_UCODE_TLV_API_CHANGES_SET: 654286441Srpaulo case IWM_UCODE_TLV_ENABLED_CAPABILITIES: 655286441Srpaulo /* ignore, not used by current driver */ 656286441Srpaulo break; 657286441Srpaulo 658286441Srpaulo default: 659286441Srpaulo device_printf(sc->sc_dev, 660286441Srpaulo "%s: unknown firmware section %d, abort\n", 661286441Srpaulo __func__, tlv_type); 662286441Srpaulo error = EINVAL; 663286441Srpaulo goto parse_out; 664286441Srpaulo } 665286441Srpaulo 666286441Srpaulo len -= roundup(tlv_len, 4); 667286441Srpaulo data += roundup(tlv_len, 4); 668286441Srpaulo } 669286441Srpaulo 670286441Srpaulo KASSERT(error == 0, ("unhandled error")); 671286441Srpaulo 672286441Srpaulo parse_out: 673286441Srpaulo if (error) { 674286441Srpaulo device_printf(sc->sc_dev, "firmware parse error %d, " 675286441Srpaulo "section type %d\n", error, tlv_type); 676286441Srpaulo } 677286441Srpaulo 678286441Srpaulo if (!(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_PM_CMD_SUPPORT)) { 679286441Srpaulo device_printf(sc->sc_dev, 680286441Srpaulo "device uses unsupported power ops\n"); 681286441Srpaulo error = ENOTSUP; 682286441Srpaulo } 683286441Srpaulo 684286441Srpaulo out: 685286441Srpaulo if (error) { 686286441Srpaulo fw->fw_status = IWM_FW_STATUS_NONE; 687286441Srpaulo if (fw->fw_rawdata != NULL) 688286441Srpaulo iwm_fw_info_free(fw); 689286441Srpaulo } else 690286441Srpaulo fw->fw_status = IWM_FW_STATUS_DONE; 691286441Srpaulo wakeup(&sc->sc_fw); 692286441Srpaulo 693286441Srpaulo return error; 694286441Srpaulo} 695286441Srpaulo 696286441Srpaulo/* 697286441Srpaulo * DMA resource routines 698286441Srpaulo */ 699286441Srpaulo 700286441Srpaulostatic void 701286441Srpauloiwm_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 702286441Srpaulo{ 703286441Srpaulo if (error != 0) 704286441Srpaulo return; 705286441Srpaulo KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs)); 706286441Srpaulo *(bus_addr_t *)arg = segs[0].ds_addr; 707286441Srpaulo} 708286441Srpaulo 709286441Srpaulostatic int 710286441Srpauloiwm_dma_contig_alloc(bus_dma_tag_t tag, struct iwm_dma_info *dma, 711286441Srpaulo bus_size_t size, bus_size_t alignment) 712286441Srpaulo{ 713286441Srpaulo int error; 714286441Srpaulo 715286441Srpaulo dma->tag = NULL; 716286441Srpaulo dma->size = size; 717286441Srpaulo 718286441Srpaulo error = bus_dma_tag_create(tag, alignment, 719286441Srpaulo 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, 720286441Srpaulo 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); 721286441Srpaulo if (error != 0) 722286441Srpaulo goto fail; 723286441Srpaulo 724286441Srpaulo error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, 725286441Srpaulo BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); 726286441Srpaulo if (error != 0) 727286441Srpaulo goto fail; 728286441Srpaulo 729286441Srpaulo error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, 730286441Srpaulo iwm_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); 731286441Srpaulo if (error != 0) 732286441Srpaulo goto fail; 733286441Srpaulo 734286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 735286441Srpaulo 736286441Srpaulo return 0; 737286441Srpaulo 738286441Srpaulofail: iwm_dma_contig_free(dma); 739286441Srpaulo return error; 740286441Srpaulo} 741286441Srpaulo 742286441Srpaulostatic void 743286441Srpauloiwm_dma_contig_free(struct iwm_dma_info *dma) 744286441Srpaulo{ 745286441Srpaulo if (dma->map != NULL) { 746286441Srpaulo if (dma->vaddr != NULL) { 747286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, 748286441Srpaulo BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 749286441Srpaulo bus_dmamap_unload(dma->tag, dma->map); 750286441Srpaulo bus_dmamem_free(dma->tag, dma->vaddr, dma->map); 751286441Srpaulo dma->vaddr = NULL; 752286441Srpaulo } 753286441Srpaulo bus_dmamap_destroy(dma->tag, dma->map); 754286441Srpaulo dma->map = NULL; 755286441Srpaulo } 756286441Srpaulo if (dma->tag != NULL) { 757286441Srpaulo bus_dma_tag_destroy(dma->tag); 758286441Srpaulo dma->tag = NULL; 759286441Srpaulo } 760286441Srpaulo 761286441Srpaulo} 762286441Srpaulo 763286441Srpaulo/* fwmem is used to load firmware onto the card */ 764286441Srpaulostatic int 765286441Srpauloiwm_alloc_fwmem(struct iwm_softc *sc) 766286441Srpaulo{ 767286441Srpaulo /* Must be aligned on a 16-byte boundary. */ 768286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->fw_dma, 769286441Srpaulo sc->sc_fwdmasegsz, 16); 770286441Srpaulo} 771286441Srpaulo 772286441Srpaulostatic void 773286441Srpauloiwm_free_fwmem(struct iwm_softc *sc) 774286441Srpaulo{ 775286441Srpaulo iwm_dma_contig_free(&sc->fw_dma); 776286441Srpaulo} 777286441Srpaulo 778286441Srpaulo/* tx scheduler rings. not used? */ 779286441Srpaulostatic int 780286441Srpauloiwm_alloc_sched(struct iwm_softc *sc) 781286441Srpaulo{ 782286441Srpaulo int rv; 783286441Srpaulo 784286441Srpaulo /* TX scheduler rings must be aligned on a 1KB boundary. */ 785286441Srpaulo rv = iwm_dma_contig_alloc(sc->sc_dmat, &sc->sched_dma, 786286441Srpaulo nitems(sc->txq) * sizeof(struct iwm_agn_scd_bc_tbl), 1024); 787286441Srpaulo return rv; 788286441Srpaulo} 789286441Srpaulo 790286441Srpaulostatic void 791286441Srpauloiwm_free_sched(struct iwm_softc *sc) 792286441Srpaulo{ 793286441Srpaulo iwm_dma_contig_free(&sc->sched_dma); 794286441Srpaulo} 795286441Srpaulo 796286441Srpaulo/* keep-warm page is used internally by the card. see iwl-fh.h for more info */ 797286441Srpaulostatic int 798286441Srpauloiwm_alloc_kw(struct iwm_softc *sc) 799286441Srpaulo{ 800286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->kw_dma, 4096, 4096); 801286441Srpaulo} 802286441Srpaulo 803286441Srpaulostatic void 804286441Srpauloiwm_free_kw(struct iwm_softc *sc) 805286441Srpaulo{ 806286441Srpaulo iwm_dma_contig_free(&sc->kw_dma); 807286441Srpaulo} 808286441Srpaulo 809286441Srpaulo/* interrupt cause table */ 810286441Srpaulostatic int 811286441Srpauloiwm_alloc_ict(struct iwm_softc *sc) 812286441Srpaulo{ 813286441Srpaulo return iwm_dma_contig_alloc(sc->sc_dmat, &sc->ict_dma, 814286441Srpaulo IWM_ICT_SIZE, 1<<IWM_ICT_PADDR_SHIFT); 815286441Srpaulo} 816286441Srpaulo 817286441Srpaulostatic void 818286441Srpauloiwm_free_ict(struct iwm_softc *sc) 819286441Srpaulo{ 820286441Srpaulo iwm_dma_contig_free(&sc->ict_dma); 821286441Srpaulo} 822286441Srpaulo 823286441Srpaulostatic int 824286441Srpauloiwm_alloc_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 825286441Srpaulo{ 826286441Srpaulo bus_size_t size; 827286441Srpaulo int i, error; 828286441Srpaulo 829286441Srpaulo ring->cur = 0; 830286441Srpaulo 831286441Srpaulo /* Allocate RX descriptors (256-byte aligned). */ 832286441Srpaulo size = IWM_RX_RING_COUNT * sizeof(uint32_t); 833286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 834286441Srpaulo if (error != 0) { 835286441Srpaulo device_printf(sc->sc_dev, 836286441Srpaulo "could not allocate RX ring DMA memory\n"); 837286441Srpaulo goto fail; 838286441Srpaulo } 839286441Srpaulo ring->desc = ring->desc_dma.vaddr; 840286441Srpaulo 841286441Srpaulo /* Allocate RX status area (16-byte aligned). */ 842286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->stat_dma, 843286441Srpaulo sizeof(*ring->stat), 16); 844286441Srpaulo if (error != 0) { 845286441Srpaulo device_printf(sc->sc_dev, 846286441Srpaulo "could not allocate RX status DMA memory\n"); 847286441Srpaulo goto fail; 848286441Srpaulo } 849286441Srpaulo ring->stat = ring->stat_dma.vaddr; 850286441Srpaulo 851286441Srpaulo /* Create RX buffer DMA tag. */ 852286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 853286441Srpaulo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 854286441Srpaulo IWM_RBUF_SIZE, 1, IWM_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL, 855286441Srpaulo &ring->data_dmat); 856286441Srpaulo if (error != 0) { 857286441Srpaulo device_printf(sc->sc_dev, 858286441Srpaulo "%s: could not create RX buf DMA tag, error %d\n", 859286441Srpaulo __func__, error); 860286441Srpaulo goto fail; 861286441Srpaulo } 862286441Srpaulo 863286441Srpaulo /* 864286441Srpaulo * Allocate and map RX buffers. 865286441Srpaulo */ 866286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 867286441Srpaulo if ((error = iwm_rx_addbuf(sc, IWM_RBUF_SIZE, i)) != 0) { 868286441Srpaulo goto fail; 869286441Srpaulo } 870286441Srpaulo } 871286441Srpaulo return 0; 872286441Srpaulo 873286441Srpaulofail: iwm_free_rx_ring(sc, ring); 874286441Srpaulo return error; 875286441Srpaulo} 876286441Srpaulo 877286441Srpaulostatic void 878286441Srpauloiwm_reset_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 879286441Srpaulo{ 880286441Srpaulo 881286441Srpaulo /* XXX print out if we can't lock the NIC? */ 882286441Srpaulo if (iwm_nic_lock(sc)) { 883286441Srpaulo /* XXX handle if RX stop doesn't finish? */ 884286441Srpaulo (void) iwm_pcie_rx_stop(sc); 885286441Srpaulo iwm_nic_unlock(sc); 886286441Srpaulo } 887287965Sadrian /* Reset the ring state */ 888286441Srpaulo ring->cur = 0; 889287965Sadrian memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 890286441Srpaulo} 891286441Srpaulo 892286441Srpaulostatic void 893286441Srpauloiwm_free_rx_ring(struct iwm_softc *sc, struct iwm_rx_ring *ring) 894286441Srpaulo{ 895286441Srpaulo int i; 896286441Srpaulo 897286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 898286441Srpaulo iwm_dma_contig_free(&ring->stat_dma); 899286441Srpaulo 900286441Srpaulo for (i = 0; i < IWM_RX_RING_COUNT; i++) { 901286441Srpaulo struct iwm_rx_data *data = &ring->data[i]; 902286441Srpaulo 903286441Srpaulo if (data->m != NULL) { 904286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 905286441Srpaulo BUS_DMASYNC_POSTREAD); 906286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 907286441Srpaulo m_freem(data->m); 908286441Srpaulo data->m = NULL; 909286441Srpaulo } 910286441Srpaulo if (data->map != NULL) { 911286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 912286441Srpaulo data->map = NULL; 913286441Srpaulo } 914286441Srpaulo } 915286441Srpaulo if (ring->data_dmat != NULL) { 916286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 917286441Srpaulo ring->data_dmat = NULL; 918286441Srpaulo } 919286441Srpaulo} 920286441Srpaulo 921286441Srpaulostatic int 922286441Srpauloiwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) 923286441Srpaulo{ 924286441Srpaulo bus_addr_t paddr; 925286441Srpaulo bus_size_t size; 926286441Srpaulo int i, error; 927286441Srpaulo 928286441Srpaulo ring->qid = qid; 929286441Srpaulo ring->queued = 0; 930286441Srpaulo ring->cur = 0; 931286441Srpaulo 932286441Srpaulo /* Allocate TX descriptors (256-byte aligned). */ 933286441Srpaulo size = IWM_TX_RING_COUNT * sizeof (struct iwm_tfd); 934286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->desc_dma, size, 256); 935286441Srpaulo if (error != 0) { 936286441Srpaulo device_printf(sc->sc_dev, 937286441Srpaulo "could not allocate TX ring DMA memory\n"); 938286441Srpaulo goto fail; 939286441Srpaulo } 940286441Srpaulo ring->desc = ring->desc_dma.vaddr; 941286441Srpaulo 942286441Srpaulo /* 943286441Srpaulo * We only use rings 0 through 9 (4 EDCA + cmd) so there is no need 944286441Srpaulo * to allocate commands space for other rings. 945286441Srpaulo */ 946286441Srpaulo if (qid > IWM_MVM_CMD_QUEUE) 947286441Srpaulo return 0; 948286441Srpaulo 949286441Srpaulo size = IWM_TX_RING_COUNT * sizeof(struct iwm_device_cmd); 950286441Srpaulo error = iwm_dma_contig_alloc(sc->sc_dmat, &ring->cmd_dma, size, 4); 951286441Srpaulo if (error != 0) { 952286441Srpaulo device_printf(sc->sc_dev, 953286441Srpaulo "could not allocate TX cmd DMA memory\n"); 954286441Srpaulo goto fail; 955286441Srpaulo } 956286441Srpaulo ring->cmd = ring->cmd_dma.vaddr; 957286441Srpaulo 958286441Srpaulo error = bus_dma_tag_create(sc->sc_dmat, 1, 0, 959286441Srpaulo BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, 960286441Srpaulo IWM_MAX_SCATTER - 1, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, 961286441Srpaulo &ring->data_dmat); 962286441Srpaulo if (error != 0) { 963286441Srpaulo device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); 964286441Srpaulo goto fail; 965286441Srpaulo } 966286441Srpaulo 967286441Srpaulo paddr = ring->cmd_dma.paddr; 968286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 969286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 970286441Srpaulo 971286441Srpaulo data->cmd_paddr = paddr; 972286441Srpaulo data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) 973286441Srpaulo + offsetof(struct iwm_tx_cmd, scratch); 974286441Srpaulo paddr += sizeof(struct iwm_device_cmd); 975286441Srpaulo 976286441Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 977286441Srpaulo if (error != 0) { 978286441Srpaulo device_printf(sc->sc_dev, 979286441Srpaulo "could not create TX buf DMA map\n"); 980286441Srpaulo goto fail; 981286441Srpaulo } 982286441Srpaulo } 983286441Srpaulo KASSERT(paddr == ring->cmd_dma.paddr + size, 984286441Srpaulo ("invalid physical address")); 985286441Srpaulo return 0; 986286441Srpaulo 987286441Srpaulofail: iwm_free_tx_ring(sc, ring); 988286441Srpaulo return error; 989286441Srpaulo} 990286441Srpaulo 991286441Srpaulostatic void 992286441Srpauloiwm_reset_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 993286441Srpaulo{ 994286441Srpaulo int i; 995286441Srpaulo 996286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 997286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 998286441Srpaulo 999286441Srpaulo if (data->m != NULL) { 1000286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1001286441Srpaulo BUS_DMASYNC_POSTWRITE); 1002286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1003286441Srpaulo m_freem(data->m); 1004286441Srpaulo data->m = NULL; 1005286441Srpaulo } 1006286441Srpaulo } 1007286441Srpaulo /* Clear TX descriptors. */ 1008286441Srpaulo memset(ring->desc, 0, ring->desc_dma.size); 1009286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 1010286441Srpaulo BUS_DMASYNC_PREWRITE); 1011286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 1012286441Srpaulo ring->queued = 0; 1013286441Srpaulo ring->cur = 0; 1014286441Srpaulo} 1015286441Srpaulo 1016286441Srpaulostatic void 1017286441Srpauloiwm_free_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring) 1018286441Srpaulo{ 1019286441Srpaulo int i; 1020286441Srpaulo 1021286441Srpaulo iwm_dma_contig_free(&ring->desc_dma); 1022286441Srpaulo iwm_dma_contig_free(&ring->cmd_dma); 1023286441Srpaulo 1024286441Srpaulo for (i = 0; i < IWM_TX_RING_COUNT; i++) { 1025286441Srpaulo struct iwm_tx_data *data = &ring->data[i]; 1026286441Srpaulo 1027286441Srpaulo if (data->m != NULL) { 1028286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 1029286441Srpaulo BUS_DMASYNC_POSTWRITE); 1030286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 1031286441Srpaulo m_freem(data->m); 1032286441Srpaulo data->m = NULL; 1033286441Srpaulo } 1034286441Srpaulo if (data->map != NULL) { 1035286441Srpaulo bus_dmamap_destroy(ring->data_dmat, data->map); 1036286441Srpaulo data->map = NULL; 1037286441Srpaulo } 1038286441Srpaulo } 1039286441Srpaulo if (ring->data_dmat != NULL) { 1040286441Srpaulo bus_dma_tag_destroy(ring->data_dmat); 1041286441Srpaulo ring->data_dmat = NULL; 1042286441Srpaulo } 1043286441Srpaulo} 1044286441Srpaulo 1045286441Srpaulo/* 1046286441Srpaulo * High-level hardware frobbing routines 1047286441Srpaulo */ 1048286441Srpaulo 1049286441Srpaulostatic void 1050286441Srpauloiwm_enable_interrupts(struct iwm_softc *sc) 1051286441Srpaulo{ 1052286441Srpaulo sc->sc_intmask = IWM_CSR_INI_SET_MASK; 1053286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1054286441Srpaulo} 1055286441Srpaulo 1056286441Srpaulostatic void 1057286441Srpauloiwm_restore_interrupts(struct iwm_softc *sc) 1058286441Srpaulo{ 1059286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, sc->sc_intmask); 1060286441Srpaulo} 1061286441Srpaulo 1062286441Srpaulostatic void 1063286441Srpauloiwm_disable_interrupts(struct iwm_softc *sc) 1064286441Srpaulo{ 1065286441Srpaulo /* disable interrupts */ 1066286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 1067286441Srpaulo 1068286441Srpaulo /* acknowledge all interrupts */ 1069286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1070286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, ~0); 1071286441Srpaulo} 1072286441Srpaulo 1073286441Srpaulostatic void 1074286441Srpauloiwm_ict_reset(struct iwm_softc *sc) 1075286441Srpaulo{ 1076286441Srpaulo iwm_disable_interrupts(sc); 1077286441Srpaulo 1078286441Srpaulo /* Reset ICT table. */ 1079286441Srpaulo memset(sc->ict_dma.vaddr, 0, IWM_ICT_SIZE); 1080286441Srpaulo sc->ict_cur = 0; 1081286441Srpaulo 1082286441Srpaulo /* Set physical address of ICT table (4KB aligned). */ 1083286441Srpaulo IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, 1084286441Srpaulo IWM_CSR_DRAM_INT_TBL_ENABLE 1085286441Srpaulo | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK 1086286441Srpaulo | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); 1087286441Srpaulo 1088286441Srpaulo /* Switch to ICT interrupt mode in driver. */ 1089286441Srpaulo sc->sc_flags |= IWM_FLAG_USE_ICT; 1090286441Srpaulo 1091286441Srpaulo /* Re-enable interrupts. */ 1092286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1093286441Srpaulo iwm_enable_interrupts(sc); 1094286441Srpaulo} 1095286441Srpaulo 1096286441Srpaulo/* iwlwifi pcie/trans.c */ 1097286441Srpaulo 1098286441Srpaulo/* 1099286441Srpaulo * Since this .. hard-resets things, it's time to actually 1100286441Srpaulo * mark the first vap (if any) as having no mac context. 1101286441Srpaulo * It's annoying, but since the driver is potentially being 1102286441Srpaulo * stop/start'ed whilst active (thanks openbsd port!) we 1103286441Srpaulo * have to correctly track this. 1104286441Srpaulo */ 1105286441Srpaulostatic void 1106286441Srpauloiwm_stop_device(struct iwm_softc *sc) 1107286441Srpaulo{ 1108287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1109286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 1110286441Srpaulo int chnl, ntries; 1111286441Srpaulo int qid; 1112286441Srpaulo 1113286441Srpaulo /* tell the device to stop sending interrupts */ 1114286441Srpaulo iwm_disable_interrupts(sc); 1115286441Srpaulo 1116286441Srpaulo /* 1117286441Srpaulo * FreeBSD-local: mark the first vap as not-uploaded, 1118286441Srpaulo * so the next transition through auth/assoc 1119286441Srpaulo * will correctly populate the MAC context. 1120286441Srpaulo */ 1121286441Srpaulo if (vap) { 1122286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 1123286441Srpaulo iv->is_uploaded = 0; 1124286441Srpaulo } 1125286441Srpaulo 1126286441Srpaulo /* device going down, Stop using ICT table */ 1127286441Srpaulo sc->sc_flags &= ~IWM_FLAG_USE_ICT; 1128286441Srpaulo 1129286441Srpaulo /* stop tx and rx. tx and rx bits, as usual, are from if_iwn */ 1130286441Srpaulo 1131286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1132286441Srpaulo 1133286441Srpaulo /* Stop all DMA channels. */ 1134286441Srpaulo if (iwm_nic_lock(sc)) { 1135286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1136286441Srpaulo IWM_WRITE(sc, 1137286441Srpaulo IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 0); 1138286441Srpaulo for (ntries = 0; ntries < 200; ntries++) { 1139286441Srpaulo uint32_t r; 1140286441Srpaulo 1141286441Srpaulo r = IWM_READ(sc, IWM_FH_TSSR_TX_STATUS_REG); 1142286441Srpaulo if (r & IWM_FH_TSSR_TX_STATUS_REG_MSK_CHNL_IDLE( 1143286441Srpaulo chnl)) 1144286441Srpaulo break; 1145286441Srpaulo DELAY(20); 1146286441Srpaulo } 1147286441Srpaulo } 1148286441Srpaulo iwm_nic_unlock(sc); 1149286441Srpaulo } 1150286441Srpaulo 1151286441Srpaulo /* Stop RX ring. */ 1152286441Srpaulo iwm_reset_rx_ring(sc, &sc->rxq); 1153286441Srpaulo 1154286441Srpaulo /* Reset all TX rings. */ 1155286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) 1156286441Srpaulo iwm_reset_tx_ring(sc, &sc->txq[qid]); 1157286441Srpaulo 1158286441Srpaulo /* 1159286441Srpaulo * Power-down device's busmaster DMA clocks 1160286441Srpaulo */ 1161286441Srpaulo iwm_write_prph(sc, IWM_APMG_CLK_DIS_REG, IWM_APMG_CLK_VAL_DMA_CLK_RQT); 1162286441Srpaulo DELAY(5); 1163286441Srpaulo 1164286441Srpaulo /* Make sure (redundant) we've released our request to stay awake */ 1165286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 1166286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 1167286441Srpaulo 1168286441Srpaulo /* Stop the device, and put it in low power state */ 1169286441Srpaulo iwm_apm_stop(sc); 1170286441Srpaulo 1171286441Srpaulo /* Upon stop, the APM issues an interrupt if HW RF kill is set. 1172286441Srpaulo * Clean again the interrupt here 1173286441Srpaulo */ 1174286441Srpaulo iwm_disable_interrupts(sc); 1175286441Srpaulo /* stop and reset the on-board processor */ 1176286441Srpaulo IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_NEVO_RESET); 1177286441Srpaulo 1178286441Srpaulo /* 1179286441Srpaulo * Even if we stop the HW, we still want the RF kill 1180286441Srpaulo * interrupt 1181286441Srpaulo */ 1182286441Srpaulo iwm_enable_rfkill_int(sc); 1183286441Srpaulo iwm_check_rfkill(sc); 1184286441Srpaulo} 1185286441Srpaulo 1186286441Srpaulo/* iwlwifi: mvm/ops.c */ 1187286441Srpaulostatic void 1188286441Srpauloiwm_mvm_nic_config(struct iwm_softc *sc) 1189286441Srpaulo{ 1190286441Srpaulo uint8_t radio_cfg_type, radio_cfg_step, radio_cfg_dash; 1191286441Srpaulo uint32_t reg_val = 0; 1192286441Srpaulo 1193286441Srpaulo radio_cfg_type = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_TYPE) >> 1194286441Srpaulo IWM_FW_PHY_CFG_RADIO_TYPE_POS; 1195286441Srpaulo radio_cfg_step = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_STEP) >> 1196286441Srpaulo IWM_FW_PHY_CFG_RADIO_STEP_POS; 1197286441Srpaulo radio_cfg_dash = (sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RADIO_DASH) >> 1198286441Srpaulo IWM_FW_PHY_CFG_RADIO_DASH_POS; 1199286441Srpaulo 1200286441Srpaulo /* SKU control */ 1201286441Srpaulo reg_val |= IWM_CSR_HW_REV_STEP(sc->sc_hw_rev) << 1202286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_STEP; 1203286441Srpaulo reg_val |= IWM_CSR_HW_REV_DASH(sc->sc_hw_rev) << 1204286441Srpaulo IWM_CSR_HW_IF_CONFIG_REG_POS_MAC_DASH; 1205286441Srpaulo 1206286441Srpaulo /* radio configuration */ 1207286441Srpaulo reg_val |= radio_cfg_type << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; 1208286441Srpaulo reg_val |= radio_cfg_step << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_STEP; 1209286441Srpaulo reg_val |= radio_cfg_dash << IWM_CSR_HW_IF_CONFIG_REG_POS_PHY_DASH; 1210286441Srpaulo 1211286441Srpaulo IWM_WRITE(sc, IWM_CSR_HW_IF_CONFIG_REG, reg_val); 1212286441Srpaulo 1213286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1214286441Srpaulo "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, 1215286441Srpaulo radio_cfg_step, radio_cfg_dash); 1216286441Srpaulo 1217286441Srpaulo /* 1218286441Srpaulo * W/A : NIC is stuck in a reset state after Early PCIe power off 1219286441Srpaulo * (PCIe power is lost before PERST# is asserted), causing ME FW 1220286441Srpaulo * to lose ownership and not being able to obtain it back. 1221286441Srpaulo */ 1222286441Srpaulo iwm_set_bits_mask_prph(sc, IWM_APMG_PS_CTRL_REG, 1223286441Srpaulo IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, 1224286441Srpaulo ~IWM_APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); 1225286441Srpaulo} 1226286441Srpaulo 1227286441Srpaulostatic int 1228286441Srpauloiwm_nic_rx_init(struct iwm_softc *sc) 1229286441Srpaulo{ 1230286441Srpaulo if (!iwm_nic_lock(sc)) 1231286441Srpaulo return EBUSY; 1232286441Srpaulo 1233286441Srpaulo /* 1234286441Srpaulo * Initialize RX ring. This is from the iwn driver. 1235286441Srpaulo */ 1236286441Srpaulo memset(sc->rxq.stat, 0, sizeof(*sc->rxq.stat)); 1237286441Srpaulo 1238286441Srpaulo /* stop DMA */ 1239286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 0); 1240286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_RBDCB_WPTR, 0); 1241286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_FLUSH_RB_REQ, 0); 1242286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RDPTR, 0); 1243286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_RBDCB_WPTR_REG, 0); 1244286441Srpaulo 1245286441Srpaulo /* Set physical address of RX ring (256-byte aligned). */ 1246286441Srpaulo IWM_WRITE(sc, 1247286441Srpaulo IWM_FH_RSCSR_CHNL0_RBDCB_BASE_REG, sc->rxq.desc_dma.paddr >> 8); 1248286441Srpaulo 1249286441Srpaulo /* Set physical address of RX status (16-byte aligned). */ 1250286441Srpaulo IWM_WRITE(sc, 1251286441Srpaulo IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); 1252286441Srpaulo 1253286441Srpaulo /* Enable RX. */ 1254286441Srpaulo /* 1255286441Srpaulo * Note: Linux driver also sets this: 1256286441Srpaulo * (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | 1257286441Srpaulo * 1258286441Srpaulo * It causes weird behavior. YMMV. 1259286441Srpaulo */ 1260286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, 1261286441Srpaulo IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | 1262286441Srpaulo IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ 1263286441Srpaulo IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | 1264286441Srpaulo IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | 1265286441Srpaulo IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); 1266286441Srpaulo 1267286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_TIMEOUT_DEF); 1268286441Srpaulo 1269286441Srpaulo /* W/A for interrupt coalescing bug in 7260 and 3160 */ 1270286441Srpaulo if (sc->host_interrupt_operation_mode) 1271286441Srpaulo IWM_SETBITS(sc, IWM_CSR_INT_COALESCING, IWM_HOST_INT_OPER_MODE); 1272286441Srpaulo 1273286441Srpaulo /* 1274286441Srpaulo * Thus sayeth el jefe (iwlwifi) via a comment: 1275286441Srpaulo * 1276286441Srpaulo * This value should initially be 0 (before preparing any 1277286441Srpaulo * RBs), should be 8 after preparing the first 8 RBs (for example) 1278286441Srpaulo */ 1279286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, 8); 1280286441Srpaulo 1281286441Srpaulo iwm_nic_unlock(sc); 1282286441Srpaulo 1283286441Srpaulo return 0; 1284286441Srpaulo} 1285286441Srpaulo 1286286441Srpaulostatic int 1287286441Srpauloiwm_nic_tx_init(struct iwm_softc *sc) 1288286441Srpaulo{ 1289286441Srpaulo int qid; 1290286441Srpaulo 1291286441Srpaulo if (!iwm_nic_lock(sc)) 1292286441Srpaulo return EBUSY; 1293286441Srpaulo 1294286441Srpaulo /* Deactivate TX scheduler. */ 1295286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0); 1296286441Srpaulo 1297286441Srpaulo /* Set physical address of "keep warm" page (16-byte aligned). */ 1298286441Srpaulo IWM_WRITE(sc, IWM_FH_KW_MEM_ADDR_REG, sc->kw_dma.paddr >> 4); 1299286441Srpaulo 1300286441Srpaulo /* Initialize TX rings. */ 1301286441Srpaulo for (qid = 0; qid < nitems(sc->txq); qid++) { 1302286441Srpaulo struct iwm_tx_ring *txq = &sc->txq[qid]; 1303286441Srpaulo 1304286441Srpaulo /* Set physical address of TX ring (256-byte aligned). */ 1305286441Srpaulo IWM_WRITE(sc, IWM_FH_MEM_CBBC_QUEUE(qid), 1306286441Srpaulo txq->desc_dma.paddr >> 8); 1307286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 1308286441Srpaulo "%s: loading ring %d descriptors (%p) at %lx\n", 1309286441Srpaulo __func__, 1310286441Srpaulo qid, txq->desc, 1311286441Srpaulo (unsigned long) (txq->desc_dma.paddr >> 8)); 1312286441Srpaulo } 1313286441Srpaulo iwm_nic_unlock(sc); 1314286441Srpaulo 1315286441Srpaulo return 0; 1316286441Srpaulo} 1317286441Srpaulo 1318286441Srpaulostatic int 1319286441Srpauloiwm_nic_init(struct iwm_softc *sc) 1320286441Srpaulo{ 1321286441Srpaulo int error; 1322286441Srpaulo 1323286441Srpaulo iwm_apm_init(sc); 1324286441Srpaulo iwm_set_pwr(sc); 1325286441Srpaulo 1326286441Srpaulo iwm_mvm_nic_config(sc); 1327286441Srpaulo 1328286441Srpaulo if ((error = iwm_nic_rx_init(sc)) != 0) 1329286441Srpaulo return error; 1330286441Srpaulo 1331286441Srpaulo /* 1332286441Srpaulo * Ditto for TX, from iwn 1333286441Srpaulo */ 1334286441Srpaulo if ((error = iwm_nic_tx_init(sc)) != 0) 1335286441Srpaulo return error; 1336286441Srpaulo 1337286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1338286441Srpaulo "%s: shadow registers enabled\n", __func__); 1339286441Srpaulo IWM_SETBITS(sc, IWM_CSR_MAC_SHADOW_REG_CTRL, 0x800fffff); 1340286441Srpaulo 1341286441Srpaulo return 0; 1342286441Srpaulo} 1343286441Srpaulo 1344286441Srpauloenum iwm_mvm_tx_fifo { 1345286441Srpaulo IWM_MVM_TX_FIFO_BK = 0, 1346286441Srpaulo IWM_MVM_TX_FIFO_BE, 1347286441Srpaulo IWM_MVM_TX_FIFO_VI, 1348286441Srpaulo IWM_MVM_TX_FIFO_VO, 1349286441Srpaulo IWM_MVM_TX_FIFO_MCAST = 5, 1350286441Srpaulo}; 1351286441Srpaulo 1352286441Srpauloconst uint8_t iwm_mvm_ac_to_tx_fifo[] = { 1353286441Srpaulo IWM_MVM_TX_FIFO_VO, 1354286441Srpaulo IWM_MVM_TX_FIFO_VI, 1355286441Srpaulo IWM_MVM_TX_FIFO_BE, 1356286441Srpaulo IWM_MVM_TX_FIFO_BK, 1357286441Srpaulo}; 1358286441Srpaulo 1359286441Srpaulostatic void 1360286441Srpauloiwm_enable_txq(struct iwm_softc *sc, int qid, int fifo) 1361286441Srpaulo{ 1362286441Srpaulo if (!iwm_nic_lock(sc)) { 1363286441Srpaulo device_printf(sc->sc_dev, 1364286441Srpaulo "%s: cannot enable txq %d\n", 1365286441Srpaulo __func__, 1366286441Srpaulo qid); 1367286441Srpaulo return; /* XXX return EBUSY */ 1368286441Srpaulo } 1369286441Srpaulo 1370286441Srpaulo /* unactivate before configuration */ 1371286441Srpaulo iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1372286441Srpaulo (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) 1373286441Srpaulo | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); 1374286441Srpaulo 1375286441Srpaulo if (qid != IWM_MVM_CMD_QUEUE) { 1376286441Srpaulo iwm_set_bits_prph(sc, IWM_SCD_QUEUECHAIN_SEL, (1 << qid)); 1377286441Srpaulo } 1378286441Srpaulo 1379286441Srpaulo iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); 1380286441Srpaulo 1381286441Srpaulo IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); 1382286441Srpaulo iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); 1383286441Srpaulo 1384286441Srpaulo iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); 1385286441Srpaulo /* Set scheduler window size and frame limit. */ 1386286441Srpaulo iwm_write_mem32(sc, 1387286441Srpaulo sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + 1388286441Srpaulo sizeof(uint32_t), 1389286441Srpaulo ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & 1390286441Srpaulo IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | 1391286441Srpaulo ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & 1392286441Srpaulo IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); 1393286441Srpaulo 1394286441Srpaulo iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), 1395286441Srpaulo (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | 1396286441Srpaulo (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | 1397286441Srpaulo (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | 1398286441Srpaulo IWM_SCD_QUEUE_STTS_REG_MSK); 1399286441Srpaulo 1400286441Srpaulo iwm_nic_unlock(sc); 1401286441Srpaulo 1402286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 1403286441Srpaulo "%s: enabled txq %d FIFO %d\n", 1404286441Srpaulo __func__, qid, fifo); 1405286441Srpaulo} 1406286441Srpaulo 1407286441Srpaulostatic int 1408286441Srpauloiwm_post_alive(struct iwm_softc *sc) 1409286441Srpaulo{ 1410286441Srpaulo int nwords; 1411286441Srpaulo int error, chnl; 1412286441Srpaulo 1413286441Srpaulo if (!iwm_nic_lock(sc)) 1414286441Srpaulo return EBUSY; 1415286441Srpaulo 1416286441Srpaulo if (sc->sched_base != iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR)) { 1417286441Srpaulo device_printf(sc->sc_dev, 1418286441Srpaulo "%s: sched addr mismatch", 1419286441Srpaulo __func__); 1420286441Srpaulo error = EINVAL; 1421286441Srpaulo goto out; 1422286441Srpaulo } 1423286441Srpaulo 1424286441Srpaulo iwm_ict_reset(sc); 1425286441Srpaulo 1426286441Srpaulo /* Clear TX scheduler state in SRAM. */ 1427286441Srpaulo nwords = (IWM_SCD_TRANS_TBL_MEM_UPPER_BOUND - 1428286441Srpaulo IWM_SCD_CONTEXT_MEM_LOWER_BOUND) 1429286441Srpaulo / sizeof(uint32_t); 1430286441Srpaulo error = iwm_write_mem(sc, 1431286441Srpaulo sc->sched_base + IWM_SCD_CONTEXT_MEM_LOWER_BOUND, 1432286441Srpaulo NULL, nwords); 1433286441Srpaulo if (error) 1434286441Srpaulo goto out; 1435286441Srpaulo 1436286441Srpaulo /* Set physical address of TX scheduler rings (1KB aligned). */ 1437286441Srpaulo iwm_write_prph(sc, IWM_SCD_DRAM_BASE_ADDR, sc->sched_dma.paddr >> 10); 1438286441Srpaulo 1439286441Srpaulo iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); 1440286441Srpaulo 1441286441Srpaulo /* enable command channel */ 1442286441Srpaulo iwm_enable_txq(sc, IWM_MVM_CMD_QUEUE, 7); 1443286441Srpaulo 1444286441Srpaulo iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); 1445286441Srpaulo 1446286441Srpaulo /* Enable DMA channels. */ 1447286441Srpaulo for (chnl = 0; chnl < IWM_FH_TCSR_CHNL_NUM; chnl++) { 1448286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(chnl), 1449286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 1450286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_ENABLE); 1451286441Srpaulo } 1452286441Srpaulo 1453286441Srpaulo IWM_SETBITS(sc, IWM_FH_TX_CHICKEN_BITS_REG, 1454286441Srpaulo IWM_FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); 1455286441Srpaulo 1456286441Srpaulo /* Enable L1-Active */ 1457286441Srpaulo iwm_clear_bits_prph(sc, IWM_APMG_PCIDEV_STT_REG, 1458286441Srpaulo IWM_APMG_PCIDEV_STT_VAL_L1_ACT_DIS); 1459286441Srpaulo 1460286441Srpaulo out: 1461286441Srpaulo iwm_nic_unlock(sc); 1462286441Srpaulo return error; 1463286441Srpaulo} 1464286441Srpaulo 1465286441Srpaulo/* 1466286441Srpaulo * NVM read access and content parsing. We do not support 1467286441Srpaulo * external NVM or writing NVM. 1468286441Srpaulo * iwlwifi/mvm/nvm.c 1469286441Srpaulo */ 1470286441Srpaulo 1471286441Srpaulo/* list of NVM sections we are allowed/need to read */ 1472286441Srpauloconst int nvm_to_read[] = { 1473286441Srpaulo IWM_NVM_SECTION_TYPE_HW, 1474286441Srpaulo IWM_NVM_SECTION_TYPE_SW, 1475286441Srpaulo IWM_NVM_SECTION_TYPE_CALIBRATION, 1476286441Srpaulo IWM_NVM_SECTION_TYPE_PRODUCTION, 1477286441Srpaulo}; 1478286441Srpaulo 1479286441Srpaulo/* Default NVM size to read */ 1480286441Srpaulo#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) 1481286441Srpaulo#define IWM_MAX_NVM_SECTION_SIZE 7000 1482286441Srpaulo 1483286441Srpaulo#define IWM_NVM_WRITE_OPCODE 1 1484286441Srpaulo#define IWM_NVM_READ_OPCODE 0 1485286441Srpaulo 1486286441Srpaulostatic int 1487286441Srpauloiwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, 1488286441Srpaulo uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) 1489286441Srpaulo{ 1490286441Srpaulo offset = 0; 1491286441Srpaulo struct iwm_nvm_access_cmd nvm_access_cmd = { 1492286441Srpaulo .offset = htole16(offset), 1493286441Srpaulo .length = htole16(length), 1494286441Srpaulo .type = htole16(section), 1495286441Srpaulo .op_code = IWM_NVM_READ_OPCODE, 1496286441Srpaulo }; 1497286441Srpaulo struct iwm_nvm_access_resp *nvm_resp; 1498286441Srpaulo struct iwm_rx_packet *pkt; 1499286441Srpaulo struct iwm_host_cmd cmd = { 1500286441Srpaulo .id = IWM_NVM_ACCESS_CMD, 1501286441Srpaulo .flags = IWM_CMD_SYNC | IWM_CMD_WANT_SKB | 1502286441Srpaulo IWM_CMD_SEND_IN_RFKILL, 1503286441Srpaulo .data = { &nvm_access_cmd, }, 1504286441Srpaulo }; 1505286441Srpaulo int ret, bytes_read, offset_read; 1506286441Srpaulo uint8_t *resp_data; 1507286441Srpaulo 1508286441Srpaulo cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); 1509286441Srpaulo 1510286441Srpaulo ret = iwm_send_cmd(sc, &cmd); 1511286441Srpaulo if (ret) 1512286441Srpaulo return ret; 1513286441Srpaulo 1514286441Srpaulo pkt = cmd.resp_pkt; 1515286441Srpaulo if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { 1516286441Srpaulo device_printf(sc->sc_dev, 1517286441Srpaulo "%s: Bad return from IWM_NVM_ACCES_COMMAND (0x%08X)\n", 1518286441Srpaulo __func__, pkt->hdr.flags); 1519286441Srpaulo ret = EIO; 1520286441Srpaulo goto exit; 1521286441Srpaulo } 1522286441Srpaulo 1523286441Srpaulo /* Extract NVM response */ 1524286441Srpaulo nvm_resp = (void *)pkt->data; 1525286441Srpaulo 1526286441Srpaulo ret = le16toh(nvm_resp->status); 1527286441Srpaulo bytes_read = le16toh(nvm_resp->length); 1528286441Srpaulo offset_read = le16toh(nvm_resp->offset); 1529286441Srpaulo resp_data = nvm_resp->data; 1530286441Srpaulo if (ret) { 1531286441Srpaulo device_printf(sc->sc_dev, 1532286441Srpaulo "%s: NVM access command failed with status %d\n", 1533286441Srpaulo __func__, ret); 1534286441Srpaulo ret = EINVAL; 1535286441Srpaulo goto exit; 1536286441Srpaulo } 1537286441Srpaulo 1538286441Srpaulo if (offset_read != offset) { 1539286441Srpaulo device_printf(sc->sc_dev, 1540286441Srpaulo "%s: NVM ACCESS response with invalid offset %d\n", 1541286441Srpaulo __func__, offset_read); 1542286441Srpaulo ret = EINVAL; 1543286441Srpaulo goto exit; 1544286441Srpaulo } 1545286441Srpaulo 1546286441Srpaulo memcpy(data + offset, resp_data, bytes_read); 1547286441Srpaulo *len = bytes_read; 1548286441Srpaulo 1549286441Srpaulo exit: 1550286441Srpaulo iwm_free_resp(sc, &cmd); 1551286441Srpaulo return ret; 1552286441Srpaulo} 1553286441Srpaulo 1554286441Srpaulo/* 1555286441Srpaulo * Reads an NVM section completely. 1556286441Srpaulo * NICs prior to 7000 family doesn't have a real NVM, but just read 1557286441Srpaulo * section 0 which is the EEPROM. Because the EEPROM reading is unlimited 1558286441Srpaulo * by uCode, we need to manually check in this case that we don't 1559286441Srpaulo * overflow and try to read more than the EEPROM size. 1560286441Srpaulo * For 7000 family NICs, we supply the maximal size we can read, and 1561286441Srpaulo * the uCode fills the response with as much data as we can, 1562286441Srpaulo * without overflowing, so no check is needed. 1563286441Srpaulo */ 1564286441Srpaulostatic int 1565286441Srpauloiwm_nvm_read_section(struct iwm_softc *sc, 1566286441Srpaulo uint16_t section, uint8_t *data, uint16_t *len) 1567286441Srpaulo{ 1568286441Srpaulo uint16_t length, seglen; 1569286441Srpaulo int error; 1570286441Srpaulo 1571286441Srpaulo /* Set nvm section read length */ 1572286441Srpaulo length = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; 1573286441Srpaulo *len = 0; 1574286441Srpaulo 1575286441Srpaulo /* Read the NVM until exhausted (reading less than requested) */ 1576286441Srpaulo while (seglen == length) { 1577286441Srpaulo error = iwm_nvm_read_chunk(sc, 1578286441Srpaulo section, *len, length, data, &seglen); 1579286441Srpaulo if (error) { 1580286441Srpaulo device_printf(sc->sc_dev, 1581286441Srpaulo "Cannot read NVM from section " 1582286441Srpaulo "%d offset %d, length %d\n", 1583286441Srpaulo section, *len, length); 1584286441Srpaulo return error; 1585286441Srpaulo } 1586286441Srpaulo *len += seglen; 1587286441Srpaulo } 1588286441Srpaulo 1589286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 1590286441Srpaulo "NVM section %d read completed\n", section); 1591286441Srpaulo return 0; 1592286441Srpaulo} 1593286441Srpaulo 1594286441Srpaulo/* 1595286441Srpaulo * BEGIN IWM_NVM_PARSE 1596286441Srpaulo */ 1597286441Srpaulo 1598286441Srpaulo/* iwlwifi/iwl-nvm-parse.c */ 1599286441Srpaulo 1600286441Srpaulo/* NVM offsets (in words) definitions */ 1601286441Srpauloenum wkp_nvm_offsets { 1602286441Srpaulo /* NVM HW-Section offset (in words) definitions */ 1603286441Srpaulo IWM_HW_ADDR = 0x15, 1604286441Srpaulo 1605286441Srpaulo/* NVM SW-Section offset (in words) definitions */ 1606286441Srpaulo IWM_NVM_SW_SECTION = 0x1C0, 1607286441Srpaulo IWM_NVM_VERSION = 0, 1608286441Srpaulo IWM_RADIO_CFG = 1, 1609286441Srpaulo IWM_SKU = 2, 1610286441Srpaulo IWM_N_HW_ADDRS = 3, 1611286441Srpaulo IWM_NVM_CHANNELS = 0x1E0 - IWM_NVM_SW_SECTION, 1612286441Srpaulo 1613286441Srpaulo/* NVM calibration section offset (in words) definitions */ 1614286441Srpaulo IWM_NVM_CALIB_SECTION = 0x2B8, 1615286441Srpaulo IWM_XTAL_CALIB = 0x316 - IWM_NVM_CALIB_SECTION 1616286441Srpaulo}; 1617286441Srpaulo 1618286441Srpaulo/* SKU Capabilities (actual values from NVM definition) */ 1619286441Srpauloenum nvm_sku_bits { 1620286441Srpaulo IWM_NVM_SKU_CAP_BAND_24GHZ = (1 << 0), 1621286441Srpaulo IWM_NVM_SKU_CAP_BAND_52GHZ = (1 << 1), 1622286441Srpaulo IWM_NVM_SKU_CAP_11N_ENABLE = (1 << 2), 1623286441Srpaulo IWM_NVM_SKU_CAP_11AC_ENABLE = (1 << 3), 1624286441Srpaulo}; 1625286441Srpaulo 1626286441Srpaulo/* radio config bits (actual values from NVM definition) */ 1627286441Srpaulo#define IWM_NVM_RF_CFG_DASH_MSK(x) (x & 0x3) /* bits 0-1 */ 1628286441Srpaulo#define IWM_NVM_RF_CFG_STEP_MSK(x) ((x >> 2) & 0x3) /* bits 2-3 */ 1629286441Srpaulo#define IWM_NVM_RF_CFG_TYPE_MSK(x) ((x >> 4) & 0x3) /* bits 4-5 */ 1630286441Srpaulo#define IWM_NVM_RF_CFG_PNUM_MSK(x) ((x >> 6) & 0x3) /* bits 6-7 */ 1631286441Srpaulo#define IWM_NVM_RF_CFG_TX_ANT_MSK(x) ((x >> 8) & 0xF) /* bits 8-11 */ 1632286441Srpaulo#define IWM_NVM_RF_CFG_RX_ANT_MSK(x) ((x >> 12) & 0xF) /* bits 12-15 */ 1633286441Srpaulo 1634286441Srpaulo#define DEFAULT_MAX_TX_POWER 16 1635286441Srpaulo 1636286441Srpaulo/** 1637286441Srpaulo * enum iwm_nvm_channel_flags - channel flags in NVM 1638286441Srpaulo * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo 1639286441Srpaulo * @IWM_NVM_CHANNEL_IBSS: usable as an IBSS channel 1640286441Srpaulo * @IWM_NVM_CHANNEL_ACTIVE: active scanning allowed 1641286441Srpaulo * @IWM_NVM_CHANNEL_RADAR: radar detection required 1642286441Srpaulo * @IWM_NVM_CHANNEL_DFS: dynamic freq selection candidate 1643286441Srpaulo * @IWM_NVM_CHANNEL_WIDE: 20 MHz channel okay (?) 1644286441Srpaulo * @IWM_NVM_CHANNEL_40MHZ: 40 MHz channel okay (?) 1645286441Srpaulo * @IWM_NVM_CHANNEL_80MHZ: 80 MHz channel okay (?) 1646286441Srpaulo * @IWM_NVM_CHANNEL_160MHZ: 160 MHz channel okay (?) 1647286441Srpaulo */ 1648286441Srpauloenum iwm_nvm_channel_flags { 1649286441Srpaulo IWM_NVM_CHANNEL_VALID = (1 << 0), 1650286441Srpaulo IWM_NVM_CHANNEL_IBSS = (1 << 1), 1651286441Srpaulo IWM_NVM_CHANNEL_ACTIVE = (1 << 3), 1652286441Srpaulo IWM_NVM_CHANNEL_RADAR = (1 << 4), 1653286441Srpaulo IWM_NVM_CHANNEL_DFS = (1 << 7), 1654286441Srpaulo IWM_NVM_CHANNEL_WIDE = (1 << 8), 1655286441Srpaulo IWM_NVM_CHANNEL_40MHZ = (1 << 9), 1656286441Srpaulo IWM_NVM_CHANNEL_80MHZ = (1 << 10), 1657286441Srpaulo IWM_NVM_CHANNEL_160MHZ = (1 << 11), 1658286441Srpaulo}; 1659286441Srpaulo 1660286441Srpaulo/* 1661286441Srpaulo * Add a channel to the net80211 channel list. 1662286441Srpaulo * 1663286441Srpaulo * ieee is the ieee channel number 1664286441Srpaulo * ch_idx is channel index. 1665286441Srpaulo * mode is the channel mode - CHAN_A, CHAN_B, CHAN_G. 1666286441Srpaulo * ch_flags is the iwm channel flags. 1667286441Srpaulo * 1668286441Srpaulo * Return 0 on OK, < 0 on error. 1669286441Srpaulo */ 1670286441Srpaulostatic int 1671286441Srpauloiwm_init_net80211_channel(struct iwm_softc *sc, int ieee, int ch_idx, 1672286441Srpaulo int mode, uint16_t ch_flags) 1673286441Srpaulo{ 1674286441Srpaulo /* XXX for now, no overflow checking! */ 1675287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1676286441Srpaulo int is_5ghz, flags; 1677286441Srpaulo struct ieee80211_channel *channel; 1678286441Srpaulo 1679286441Srpaulo channel = &ic->ic_channels[ic->ic_nchans++]; 1680286441Srpaulo channel->ic_ieee = ieee; 1681286441Srpaulo 1682286441Srpaulo is_5ghz = ch_idx >= IWM_NUM_2GHZ_CHANNELS; 1683286441Srpaulo if (!is_5ghz) { 1684286441Srpaulo flags = IEEE80211_CHAN_2GHZ; 1685286441Srpaulo channel->ic_flags = mode; 1686286441Srpaulo } else { 1687286441Srpaulo flags = IEEE80211_CHAN_5GHZ; 1688286441Srpaulo channel->ic_flags = mode; 1689286441Srpaulo } 1690286441Srpaulo channel->ic_freq = ieee80211_ieee2mhz(ieee, flags); 1691286441Srpaulo 1692286441Srpaulo if (!(ch_flags & IWM_NVM_CHANNEL_ACTIVE)) 1693286441Srpaulo channel->ic_flags |= IEEE80211_CHAN_PASSIVE; 1694286441Srpaulo return (0); 1695286441Srpaulo} 1696286441Srpaulo 1697286441Srpaulostatic void 1698286441Srpauloiwm_init_channel_map(struct iwm_softc *sc, const uint16_t * const nvm_ch_flags) 1699286441Srpaulo{ 1700287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 1701286441Srpaulo struct iwm_nvm_data *data = &sc->sc_nvm; 1702286441Srpaulo int ch_idx; 1703286441Srpaulo uint16_t ch_flags; 1704286441Srpaulo int hw_value; 1705286441Srpaulo 1706286441Srpaulo for (ch_idx = 0; ch_idx < nitems(iwm_nvm_channels); ch_idx++) { 1707286441Srpaulo ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); 1708286441Srpaulo 1709286441Srpaulo if (ch_idx >= IWM_NUM_2GHZ_CHANNELS && 1710286441Srpaulo !data->sku_cap_band_52GHz_enable) 1711286441Srpaulo ch_flags &= ~IWM_NVM_CHANNEL_VALID; 1712286441Srpaulo 1713286441Srpaulo if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { 1714286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1715286441Srpaulo "Ch. %d Flags %x [%sGHz] - No traffic\n", 1716286441Srpaulo iwm_nvm_channels[ch_idx], 1717286441Srpaulo ch_flags, 1718286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1719286441Srpaulo "5.2" : "2.4"); 1720286441Srpaulo continue; 1721286441Srpaulo } 1722286441Srpaulo 1723286441Srpaulo hw_value = iwm_nvm_channels[ch_idx]; 1724286441Srpaulo 1725286441Srpaulo /* 5GHz? */ 1726286441Srpaulo if (ch_idx >= IWM_NUM_2GHZ_CHANNELS) { 1727286441Srpaulo (void) iwm_init_net80211_channel(sc, hw_value, 1728286441Srpaulo ch_idx, 1729286441Srpaulo IEEE80211_CHAN_A, 1730286441Srpaulo ch_flags); 1731286441Srpaulo } else { 1732286441Srpaulo (void) iwm_init_net80211_channel(sc, hw_value, 1733286441Srpaulo ch_idx, 1734286441Srpaulo IEEE80211_CHAN_B, 1735286441Srpaulo ch_flags); 1736286441Srpaulo /* If it's not channel 13, also add 11g */ 1737286441Srpaulo if (hw_value != 13) 1738286441Srpaulo (void) iwm_init_net80211_channel(sc, hw_value, 1739286441Srpaulo ch_idx, 1740286441Srpaulo IEEE80211_CHAN_G, 1741286441Srpaulo ch_flags); 1742286441Srpaulo } 1743286441Srpaulo 1744286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1745286441Srpaulo "Ch. %d Flags %x [%sGHz] - Added\n", 1746286441Srpaulo iwm_nvm_channels[ch_idx], 1747286441Srpaulo ch_flags, 1748286441Srpaulo (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? 1749286441Srpaulo "5.2" : "2.4"); 1750286441Srpaulo } 1751286441Srpaulo ieee80211_sort_channels(ic->ic_channels, ic->ic_nchans); 1752286441Srpaulo} 1753286441Srpaulo 1754286441Srpaulostatic int 1755286441Srpauloiwm_parse_nvm_data(struct iwm_softc *sc, 1756286441Srpaulo const uint16_t *nvm_hw, const uint16_t *nvm_sw, 1757286441Srpaulo const uint16_t *nvm_calib, uint8_t tx_chains, uint8_t rx_chains) 1758286441Srpaulo{ 1759286441Srpaulo struct iwm_nvm_data *data = &sc->sc_nvm; 1760286441Srpaulo uint8_t hw_addr[IEEE80211_ADDR_LEN]; 1761286441Srpaulo uint16_t radio_cfg, sku; 1762286441Srpaulo 1763286441Srpaulo data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION); 1764286441Srpaulo 1765286441Srpaulo radio_cfg = le16_to_cpup(nvm_sw + IWM_RADIO_CFG); 1766286441Srpaulo data->radio_cfg_type = IWM_NVM_RF_CFG_TYPE_MSK(radio_cfg); 1767286441Srpaulo data->radio_cfg_step = IWM_NVM_RF_CFG_STEP_MSK(radio_cfg); 1768286441Srpaulo data->radio_cfg_dash = IWM_NVM_RF_CFG_DASH_MSK(radio_cfg); 1769286441Srpaulo data->radio_cfg_pnum = IWM_NVM_RF_CFG_PNUM_MSK(radio_cfg); 1770286441Srpaulo data->valid_tx_ant = IWM_NVM_RF_CFG_TX_ANT_MSK(radio_cfg); 1771286441Srpaulo data->valid_rx_ant = IWM_NVM_RF_CFG_RX_ANT_MSK(radio_cfg); 1772286441Srpaulo 1773286441Srpaulo sku = le16_to_cpup(nvm_sw + IWM_SKU); 1774286441Srpaulo data->sku_cap_band_24GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_24GHZ; 1775286441Srpaulo data->sku_cap_band_52GHz_enable = sku & IWM_NVM_SKU_CAP_BAND_52GHZ; 1776286441Srpaulo data->sku_cap_11n_enable = 0; 1777286441Srpaulo 1778286441Srpaulo if (!data->valid_tx_ant || !data->valid_rx_ant) { 1779286441Srpaulo device_printf(sc->sc_dev, 1780286441Srpaulo "%s: invalid antennas (0x%x, 0x%x)\n", 1781286441Srpaulo __func__, data->valid_tx_ant, 1782286441Srpaulo data->valid_rx_ant); 1783286441Srpaulo return EINVAL; 1784286441Srpaulo } 1785286441Srpaulo 1786286441Srpaulo data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); 1787286441Srpaulo 1788286441Srpaulo data->xtal_calib[0] = *(nvm_calib + IWM_XTAL_CALIB); 1789286441Srpaulo data->xtal_calib[1] = *(nvm_calib + IWM_XTAL_CALIB + 1); 1790286441Srpaulo 1791286441Srpaulo /* The byte order is little endian 16 bit, meaning 214365 */ 1792286441Srpaulo IEEE80211_ADDR_COPY(hw_addr, nvm_hw + IWM_HW_ADDR); 1793286441Srpaulo data->hw_addr[0] = hw_addr[1]; 1794286441Srpaulo data->hw_addr[1] = hw_addr[0]; 1795286441Srpaulo data->hw_addr[2] = hw_addr[3]; 1796286441Srpaulo data->hw_addr[3] = hw_addr[2]; 1797286441Srpaulo data->hw_addr[4] = hw_addr[5]; 1798286441Srpaulo data->hw_addr[5] = hw_addr[4]; 1799286441Srpaulo 1800286441Srpaulo iwm_init_channel_map(sc, &nvm_sw[IWM_NVM_CHANNELS]); 1801286441Srpaulo data->calib_version = 255; /* TODO: 1802286441Srpaulo this value will prevent some checks from 1803286441Srpaulo failing, we need to check if this 1804286441Srpaulo field is still needed, and if it does, 1805286441Srpaulo where is it in the NVM */ 1806286441Srpaulo 1807286441Srpaulo return 0; 1808286441Srpaulo} 1809286441Srpaulo 1810286441Srpaulo/* 1811286441Srpaulo * END NVM PARSE 1812286441Srpaulo */ 1813286441Srpaulo 1814286441Srpaulostruct iwm_nvm_section { 1815286441Srpaulo uint16_t length; 1816286441Srpaulo const uint8_t *data; 1817286441Srpaulo}; 1818286441Srpaulo 1819286441Srpaulostatic int 1820286441Srpauloiwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) 1821286441Srpaulo{ 1822286441Srpaulo const uint16_t *hw, *sw, *calib; 1823286441Srpaulo 1824286441Srpaulo /* Checking for required sections */ 1825286441Srpaulo if (!sections[IWM_NVM_SECTION_TYPE_SW].data || 1826286441Srpaulo !sections[IWM_NVM_SECTION_TYPE_HW].data) { 1827286441Srpaulo device_printf(sc->sc_dev, 1828286441Srpaulo "%s: Can't parse empty NVM sections\n", 1829286441Srpaulo __func__); 1830286441Srpaulo return ENOENT; 1831286441Srpaulo } 1832286441Srpaulo 1833286441Srpaulo hw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_HW].data; 1834286441Srpaulo sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; 1835286441Srpaulo calib = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; 1836286441Srpaulo return iwm_parse_nvm_data(sc, hw, sw, calib, 1837286441Srpaulo IWM_FW_VALID_TX_ANT(sc), IWM_FW_VALID_RX_ANT(sc)); 1838286441Srpaulo} 1839286441Srpaulo 1840286441Srpaulostatic int 1841286441Srpauloiwm_nvm_init(struct iwm_softc *sc) 1842286441Srpaulo{ 1843286441Srpaulo struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS]; 1844286441Srpaulo int i, section, error; 1845286441Srpaulo uint16_t len; 1846286441Srpaulo uint8_t *nvm_buffer, *temp; 1847286441Srpaulo 1848286441Srpaulo /* Read From FW NVM */ 1849286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_EEPROM, 1850286441Srpaulo "%s: Read NVM\n", 1851286441Srpaulo __func__); 1852286441Srpaulo 1853286441Srpaulo /* TODO: find correct NVM max size for a section */ 1854286441Srpaulo nvm_buffer = malloc(IWM_OTP_LOW_IMAGE_SIZE, M_DEVBUF, M_NOWAIT); 1855286441Srpaulo if (nvm_buffer == NULL) 1856286441Srpaulo return (ENOMEM); 1857286441Srpaulo for (i = 0; i < nitems(nvm_to_read); i++) { 1858286441Srpaulo section = nvm_to_read[i]; 1859286441Srpaulo KASSERT(section <= nitems(nvm_sections), 1860286441Srpaulo ("too many sections")); 1861286441Srpaulo 1862286441Srpaulo error = iwm_nvm_read_section(sc, section, nvm_buffer, &len); 1863286441Srpaulo if (error) 1864286441Srpaulo break; 1865286441Srpaulo 1866286441Srpaulo temp = malloc(len, M_DEVBUF, M_NOWAIT); 1867286441Srpaulo if (temp == NULL) { 1868286441Srpaulo error = ENOMEM; 1869286441Srpaulo break; 1870286441Srpaulo } 1871286441Srpaulo memcpy(temp, nvm_buffer, len); 1872286441Srpaulo nvm_sections[section].data = temp; 1873286441Srpaulo nvm_sections[section].length = len; 1874286441Srpaulo } 1875286441Srpaulo free(nvm_buffer, M_DEVBUF); 1876286441Srpaulo if (error) 1877286441Srpaulo return error; 1878286441Srpaulo 1879286441Srpaulo return iwm_parse_nvm_sections(sc, nvm_sections); 1880286441Srpaulo} 1881286441Srpaulo 1882286441Srpaulo/* 1883286441Srpaulo * Firmware loading gunk. This is kind of a weird hybrid between the 1884286441Srpaulo * iwn driver and the Linux iwlwifi driver. 1885286441Srpaulo */ 1886286441Srpaulo 1887286441Srpaulostatic int 1888286441Srpauloiwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, 1889286441Srpaulo const uint8_t *section, uint32_t byte_cnt) 1890286441Srpaulo{ 1891286441Srpaulo struct iwm_dma_info *dma = &sc->fw_dma; 1892286441Srpaulo int error; 1893286441Srpaulo 1894286441Srpaulo /* Copy firmware section into pre-allocated DMA-safe memory. */ 1895286441Srpaulo memcpy(dma->vaddr, section, byte_cnt); 1896286441Srpaulo bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); 1897286441Srpaulo 1898286441Srpaulo if (!iwm_nic_lock(sc)) 1899286441Srpaulo return EBUSY; 1900286441Srpaulo 1901286441Srpaulo sc->sc_fw_chunk_done = 0; 1902286441Srpaulo 1903286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 1904286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); 1905286441Srpaulo IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), 1906286441Srpaulo dst_addr); 1907286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL0_REG(IWM_FH_SRVC_CHNL), 1908286441Srpaulo dma->paddr & IWM_FH_MEM_TFDIB_DRAM_ADDR_LSB_MSK); 1909286441Srpaulo IWM_WRITE(sc, IWM_FH_TFDIB_CTRL1_REG(IWM_FH_SRVC_CHNL), 1910286441Srpaulo (iwm_get_dma_hi_addr(dma->paddr) 1911286441Srpaulo << IWM_FH_MEM_TFDIB_REG1_ADDR_BITSHIFT) | byte_cnt); 1912286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_BUF_STS_REG(IWM_FH_SRVC_CHNL), 1913286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_NUM | 1914286441Srpaulo 1 << IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_POS_TB_IDX | 1915286441Srpaulo IWM_FH_TCSR_CHNL_TX_BUF_STS_REG_VAL_TFDB_VALID); 1916286441Srpaulo IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), 1917286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_ENABLE | 1918286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CREDIT_DISABLE | 1919286441Srpaulo IWM_FH_TCSR_TX_CONFIG_REG_VAL_CIRQ_HOST_ENDTFD); 1920286441Srpaulo 1921286441Srpaulo iwm_nic_unlock(sc); 1922286441Srpaulo 1923286441Srpaulo /* wait 1s for this segment to load */ 1924286441Srpaulo while (!sc->sc_fw_chunk_done) 1925286441Srpaulo if ((error = msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfw", hz)) != 0) 1926286441Srpaulo break; 1927286441Srpaulo 1928286441Srpaulo return error; 1929286441Srpaulo} 1930286441Srpaulo 1931286441Srpaulostatic int 1932286441Srpauloiwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 1933286441Srpaulo{ 1934286441Srpaulo struct iwm_fw_sects *fws; 1935286441Srpaulo int error, i, w; 1936286441Srpaulo const void *data; 1937286441Srpaulo uint32_t dlen; 1938286441Srpaulo uint32_t offset; 1939286441Srpaulo 1940286441Srpaulo sc->sc_uc.uc_intr = 0; 1941286441Srpaulo 1942286441Srpaulo fws = &sc->sc_fw.fw_sects[ucode_type]; 1943286441Srpaulo for (i = 0; i < fws->fw_count; i++) { 1944286441Srpaulo data = fws->fw_sect[i].fws_data; 1945286441Srpaulo dlen = fws->fw_sect[i].fws_len; 1946286441Srpaulo offset = fws->fw_sect[i].fws_devoff; 1947286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_FIRMWARE_TLV, 1948286441Srpaulo "LOAD FIRMWARE type %d offset %u len %d\n", 1949286441Srpaulo ucode_type, offset, dlen); 1950286441Srpaulo error = iwm_firmware_load_chunk(sc, offset, data, dlen); 1951286441Srpaulo if (error) { 1952286441Srpaulo device_printf(sc->sc_dev, 1953286441Srpaulo "%s: chunk %u of %u returned error %02d\n", 1954286441Srpaulo __func__, i, fws->fw_count, error); 1955286441Srpaulo return error; 1956286441Srpaulo } 1957286441Srpaulo } 1958286441Srpaulo 1959286441Srpaulo /* wait for the firmware to load */ 1960286441Srpaulo IWM_WRITE(sc, IWM_CSR_RESET, 0); 1961286441Srpaulo 1962286441Srpaulo for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { 1963286441Srpaulo error = msleep(&sc->sc_uc, &sc->sc_mtx, 0, "iwmuc", hz/10); 1964286441Srpaulo } 1965286441Srpaulo 1966286441Srpaulo return error; 1967286441Srpaulo} 1968286441Srpaulo 1969286441Srpaulo/* iwlwifi: pcie/trans.c */ 1970286441Srpaulostatic int 1971286441Srpauloiwm_start_fw(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) 1972286441Srpaulo{ 1973286441Srpaulo int error; 1974286441Srpaulo 1975286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1976286441Srpaulo 1977286441Srpaulo if ((error = iwm_nic_init(sc)) != 0) { 1978286441Srpaulo device_printf(sc->sc_dev, "unable to init nic\n"); 1979286441Srpaulo return error; 1980286441Srpaulo } 1981286441Srpaulo 1982286441Srpaulo /* make sure rfkill handshake bits are cleared */ 1983286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 1984286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, 1985286441Srpaulo IWM_CSR_UCODE_DRV_GP1_BIT_CMD_BLOCKED); 1986286441Srpaulo 1987286441Srpaulo /* clear (again), then enable host interrupts */ 1988286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, ~0); 1989286441Srpaulo iwm_enable_interrupts(sc); 1990286441Srpaulo 1991286441Srpaulo /* really make sure rfkill handshake bits are cleared */ 1992286441Srpaulo /* maybe we should write a few times more? just to make sure */ 1993286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 1994286441Srpaulo IWM_WRITE(sc, IWM_CSR_UCODE_DRV_GP1_CLR, IWM_CSR_UCODE_SW_BIT_RFKILL); 1995286441Srpaulo 1996286441Srpaulo /* Load the given image to the HW */ 1997286441Srpaulo return iwm_load_firmware(sc, ucode_type); 1998286441Srpaulo} 1999286441Srpaulo 2000286441Srpaulostatic int 2001286441Srpauloiwm_fw_alive(struct iwm_softc *sc, uint32_t sched_base) 2002286441Srpaulo{ 2003286441Srpaulo return iwm_post_alive(sc); 2004286441Srpaulo} 2005286441Srpaulo 2006286441Srpaulostatic int 2007286441Srpauloiwm_send_tx_ant_cfg(struct iwm_softc *sc, uint8_t valid_tx_ant) 2008286441Srpaulo{ 2009286441Srpaulo struct iwm_tx_ant_cfg_cmd tx_ant_cmd = { 2010286441Srpaulo .valid = htole32(valid_tx_ant), 2011286441Srpaulo }; 2012286441Srpaulo 2013286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_TX_ANT_CONFIGURATION_CMD, 2014286441Srpaulo IWM_CMD_SYNC, sizeof(tx_ant_cmd), &tx_ant_cmd); 2015286441Srpaulo} 2016286441Srpaulo 2017286441Srpaulo/* iwlwifi: mvm/fw.c */ 2018286441Srpaulostatic int 2019286441Srpauloiwm_send_phy_cfg_cmd(struct iwm_softc *sc) 2020286441Srpaulo{ 2021286441Srpaulo struct iwm_phy_cfg_cmd phy_cfg_cmd; 2022286441Srpaulo enum iwm_ucode_type ucode_type = sc->sc_uc_current; 2023286441Srpaulo 2024286441Srpaulo /* Set parameters */ 2025286441Srpaulo phy_cfg_cmd.phy_cfg = htole32(sc->sc_fw_phy_config); 2026286441Srpaulo phy_cfg_cmd.calib_control.event_trigger = 2027286441Srpaulo sc->sc_default_calib[ucode_type].event_trigger; 2028286441Srpaulo phy_cfg_cmd.calib_control.flow_trigger = 2029286441Srpaulo sc->sc_default_calib[ucode_type].flow_trigger; 2030286441Srpaulo 2031286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, 2032286441Srpaulo "Sending Phy CFG command: 0x%x\n", phy_cfg_cmd.phy_cfg); 2033286441Srpaulo return iwm_mvm_send_cmd_pdu(sc, IWM_PHY_CONFIGURATION_CMD, IWM_CMD_SYNC, 2034286441Srpaulo sizeof(phy_cfg_cmd), &phy_cfg_cmd); 2035286441Srpaulo} 2036286441Srpaulo 2037286441Srpaulostatic int 2038286441Srpauloiwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, 2039286441Srpaulo enum iwm_ucode_type ucode_type) 2040286441Srpaulo{ 2041286441Srpaulo enum iwm_ucode_type old_type = sc->sc_uc_current; 2042286441Srpaulo int error; 2043286441Srpaulo 2044286441Srpaulo if ((error = iwm_read_firmware(sc, ucode_type)) != 0) 2045286441Srpaulo return error; 2046286441Srpaulo 2047286441Srpaulo sc->sc_uc_current = ucode_type; 2048286441Srpaulo error = iwm_start_fw(sc, ucode_type); 2049286441Srpaulo if (error) { 2050286441Srpaulo sc->sc_uc_current = old_type; 2051286441Srpaulo return error; 2052286441Srpaulo } 2053286441Srpaulo 2054286441Srpaulo return iwm_fw_alive(sc, sc->sched_base); 2055286441Srpaulo} 2056286441Srpaulo 2057286441Srpaulo/* 2058286441Srpaulo * mvm misc bits 2059286441Srpaulo */ 2060286441Srpaulo 2061286441Srpaulo/* 2062286441Srpaulo * follows iwlwifi/fw.c 2063286441Srpaulo */ 2064286441Srpaulostatic int 2065286441Srpauloiwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) 2066286441Srpaulo{ 2067286441Srpaulo int error; 2068286441Srpaulo 2069286441Srpaulo /* do not operate with rfkill switch turned on */ 2070286441Srpaulo if ((sc->sc_flags & IWM_FLAG_RFKILL) && !justnvm) { 2071286441Srpaulo device_printf(sc->sc_dev, 2072286441Srpaulo "radio is disabled by hardware switch\n"); 2073286441Srpaulo return EPERM; 2074286441Srpaulo } 2075286441Srpaulo 2076286441Srpaulo sc->sc_init_complete = 0; 2077286441Srpaulo if ((error = iwm_mvm_load_ucode_wait_alive(sc, 2078286441Srpaulo IWM_UCODE_TYPE_INIT)) != 0) 2079286441Srpaulo return error; 2080286441Srpaulo 2081286441Srpaulo if (justnvm) { 2082286441Srpaulo if ((error = iwm_nvm_init(sc)) != 0) { 2083286441Srpaulo device_printf(sc->sc_dev, "failed to read nvm\n"); 2084286441Srpaulo return error; 2085286441Srpaulo } 2086287197Sglebius IEEE80211_ADDR_COPY(sc->sc_ic.ic_macaddr, &sc->sc_nvm.hw_addr); 2087286441Srpaulo 2088286441Srpaulo sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd) 2089286441Srpaulo + sc->sc_capa_max_probe_len 2090286441Srpaulo + IWM_MAX_NUM_SCAN_CHANNELS 2091286441Srpaulo * sizeof(struct iwm_scan_channel); 2092286441Srpaulo sc->sc_scan_cmd = malloc(sc->sc_scan_cmd_len, M_DEVBUF, 2093286441Srpaulo M_NOWAIT); 2094286441Srpaulo if (sc->sc_scan_cmd == NULL) 2095286441Srpaulo return (ENOMEM); 2096286441Srpaulo 2097286441Srpaulo return 0; 2098286441Srpaulo } 2099286441Srpaulo 2100286441Srpaulo /* Send TX valid antennas before triggering calibrations */ 2101286441Srpaulo if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) 2102286441Srpaulo return error; 2103286441Srpaulo 2104286441Srpaulo /* 2105286441Srpaulo * Send phy configurations command to init uCode 2106286441Srpaulo * to start the 16.0 uCode init image internal calibrations. 2107286441Srpaulo */ 2108286441Srpaulo if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { 2109286441Srpaulo device_printf(sc->sc_dev, 2110286441Srpaulo "%s: failed to run internal calibration: %d\n", 2111286441Srpaulo __func__, error); 2112286441Srpaulo return error; 2113286441Srpaulo } 2114286441Srpaulo 2115286441Srpaulo /* 2116286441Srpaulo * Nothing to do but wait for the init complete notification 2117286441Srpaulo * from the firmware 2118286441Srpaulo */ 2119286441Srpaulo while (!sc->sc_init_complete) 2120286441Srpaulo if ((error = msleep(&sc->sc_init_complete, &sc->sc_mtx, 2121286441Srpaulo 0, "iwminit", 2*hz)) != 0) 2122286441Srpaulo break; 2123286441Srpaulo 2124286441Srpaulo return error; 2125286441Srpaulo} 2126286441Srpaulo 2127286441Srpaulo/* 2128286441Srpaulo * receive side 2129286441Srpaulo */ 2130286441Srpaulo 2131286441Srpaulo/* (re)stock rx ring, called at init-time and at runtime */ 2132286441Srpaulostatic int 2133286441Srpauloiwm_rx_addbuf(struct iwm_softc *sc, int size, int idx) 2134286441Srpaulo{ 2135286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 2136286441Srpaulo struct iwm_rx_data *data = &ring->data[idx]; 2137286441Srpaulo struct mbuf *m; 2138286441Srpaulo int error; 2139286441Srpaulo bus_addr_t paddr; 2140286441Srpaulo 2141286441Srpaulo m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); 2142286441Srpaulo if (m == NULL) 2143286441Srpaulo return ENOBUFS; 2144286441Srpaulo 2145286441Srpaulo if (data->m != NULL) 2146286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2147286441Srpaulo 2148286441Srpaulo m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; 2149286441Srpaulo error = bus_dmamap_create(ring->data_dmat, 0, &data->map); 2150286441Srpaulo if (error != 0) { 2151286441Srpaulo device_printf(sc->sc_dev, 2152286441Srpaulo "%s: could not create RX buf DMA map, error %d\n", 2153286441Srpaulo __func__, error); 2154286441Srpaulo goto fail; 2155286441Srpaulo } 2156286441Srpaulo data->m = m; 2157286441Srpaulo error = bus_dmamap_load(ring->data_dmat, data->map, 2158286441Srpaulo mtod(data->m, void *), IWM_RBUF_SIZE, iwm_dma_map_addr, 2159286441Srpaulo &paddr, BUS_DMA_NOWAIT); 2160286441Srpaulo if (error != 0 && error != EFBIG) { 2161286441Srpaulo device_printf(sc->sc_dev, 2162286441Srpaulo "%s: can't not map mbuf, error %d\n", __func__, 2163286441Srpaulo error); 2164286441Srpaulo goto fail; 2165286441Srpaulo } 2166286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREREAD); 2167286441Srpaulo 2168286441Srpaulo /* Update RX descriptor. */ 2169286441Srpaulo ring->desc[idx] = htole32(paddr >> 8); 2170286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2171286441Srpaulo BUS_DMASYNC_PREWRITE); 2172286441Srpaulo 2173286441Srpaulo return 0; 2174286441Srpaulofail: 2175286441Srpaulo return error; 2176286441Srpaulo} 2177286441Srpaulo 2178286441Srpaulo/* iwlwifi: mvm/rx.c */ 2179286441Srpaulo#define IWM_RSSI_OFFSET 50 2180286441Srpaulostatic int 2181286441Srpauloiwm_mvm_calc_rssi(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2182286441Srpaulo{ 2183286441Srpaulo int rssi_a, rssi_b, rssi_a_dbm, rssi_b_dbm, max_rssi_dbm; 2184286441Srpaulo uint32_t agc_a, agc_b; 2185286441Srpaulo uint32_t val; 2186286441Srpaulo 2187286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_AGC_IDX]); 2188286441Srpaulo agc_a = (val & IWM_OFDM_AGC_A_MSK) >> IWM_OFDM_AGC_A_POS; 2189286441Srpaulo agc_b = (val & IWM_OFDM_AGC_B_MSK) >> IWM_OFDM_AGC_B_POS; 2190286441Srpaulo 2191286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_RSSI_AB_IDX]); 2192286441Srpaulo rssi_a = (val & IWM_OFDM_RSSI_INBAND_A_MSK) >> IWM_OFDM_RSSI_A_POS; 2193286441Srpaulo rssi_b = (val & IWM_OFDM_RSSI_INBAND_B_MSK) >> IWM_OFDM_RSSI_B_POS; 2194286441Srpaulo 2195286441Srpaulo /* 2196286441Srpaulo * dBm = rssi dB - agc dB - constant. 2197286441Srpaulo * Higher AGC (higher radio gain) means lower signal. 2198286441Srpaulo */ 2199286441Srpaulo rssi_a_dbm = rssi_a - IWM_RSSI_OFFSET - agc_a; 2200286441Srpaulo rssi_b_dbm = rssi_b - IWM_RSSI_OFFSET - agc_b; 2201286441Srpaulo max_rssi_dbm = MAX(rssi_a_dbm, rssi_b_dbm); 2202286441Srpaulo 2203286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2204286441Srpaulo "Rssi In A %d B %d Max %d AGCA %d AGCB %d\n", 2205286441Srpaulo rssi_a_dbm, rssi_b_dbm, max_rssi_dbm, agc_a, agc_b); 2206286441Srpaulo 2207286441Srpaulo return max_rssi_dbm; 2208286441Srpaulo} 2209286441Srpaulo 2210286441Srpaulo/* iwlwifi: mvm/rx.c */ 2211286441Srpaulo/* 2212286441Srpaulo * iwm_mvm_get_signal_strength - use new rx PHY INFO API 2213286441Srpaulo * values are reported by the fw as positive values - need to negate 2214286441Srpaulo * to obtain their dBM. Account for missing antennas by replacing 0 2215286441Srpaulo * values by -256dBm: practically 0 power and a non-feasible 8 bit value. 2216286441Srpaulo */ 2217286441Srpaulostatic int 2218286441Srpauloiwm_mvm_get_signal_strength(struct iwm_softc *sc, struct iwm_rx_phy_info *phy_info) 2219286441Srpaulo{ 2220286441Srpaulo int energy_a, energy_b, energy_c, max_energy; 2221286441Srpaulo uint32_t val; 2222286441Srpaulo 2223286441Srpaulo val = le32toh(phy_info->non_cfg_phy[IWM_RX_INFO_ENERGY_ANT_ABC_IDX]); 2224286441Srpaulo energy_a = (val & IWM_RX_INFO_ENERGY_ANT_A_MSK) >> 2225286441Srpaulo IWM_RX_INFO_ENERGY_ANT_A_POS; 2226286441Srpaulo energy_a = energy_a ? -energy_a : -256; 2227286441Srpaulo energy_b = (val & IWM_RX_INFO_ENERGY_ANT_B_MSK) >> 2228286441Srpaulo IWM_RX_INFO_ENERGY_ANT_B_POS; 2229286441Srpaulo energy_b = energy_b ? -energy_b : -256; 2230286441Srpaulo energy_c = (val & IWM_RX_INFO_ENERGY_ANT_C_MSK) >> 2231286441Srpaulo IWM_RX_INFO_ENERGY_ANT_C_POS; 2232286441Srpaulo energy_c = energy_c ? -energy_c : -256; 2233286441Srpaulo max_energy = MAX(energy_a, energy_b); 2234286441Srpaulo max_energy = MAX(max_energy, energy_c); 2235286441Srpaulo 2236286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2237286441Srpaulo "energy In A %d B %d C %d , and max %d\n", 2238286441Srpaulo energy_a, energy_b, energy_c, max_energy); 2239286441Srpaulo 2240286441Srpaulo return max_energy; 2241286441Srpaulo} 2242286441Srpaulo 2243286441Srpaulostatic void 2244286441Srpauloiwm_mvm_rx_rx_phy_cmd(struct iwm_softc *sc, 2245286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2246286441Srpaulo{ 2247286441Srpaulo struct iwm_rx_phy_info *phy_info = (void *)pkt->data; 2248286441Srpaulo 2249286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "received PHY stats\n"); 2250286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2251286441Srpaulo 2252286441Srpaulo memcpy(&sc->sc_last_phy_info, phy_info, sizeof(sc->sc_last_phy_info)); 2253286441Srpaulo} 2254286441Srpaulo 2255286441Srpaulo/* 2256286441Srpaulo * Retrieve the average noise (in dBm) among receivers. 2257286441Srpaulo */ 2258286441Srpaulostatic int 2259286441Srpauloiwm_get_noise(const struct iwm_mvm_statistics_rx_non_phy *stats) 2260286441Srpaulo{ 2261286441Srpaulo int i, total, nbant, noise; 2262286441Srpaulo 2263286441Srpaulo total = nbant = noise = 0; 2264286441Srpaulo for (i = 0; i < 3; i++) { 2265286441Srpaulo noise = le32toh(stats->beacon_silence_rssi[i]) & 0xff; 2266286441Srpaulo if (noise) { 2267286441Srpaulo total += noise; 2268286441Srpaulo nbant++; 2269286441Srpaulo } 2270286441Srpaulo } 2271286441Srpaulo 2272286441Srpaulo /* There should be at least one antenna but check anyway. */ 2273286441Srpaulo return (nbant == 0) ? -127 : (total / nbant) - 107; 2274286441Srpaulo} 2275286441Srpaulo 2276286441Srpaulo/* 2277286441Srpaulo * iwm_mvm_rx_rx_mpdu - IWM_REPLY_RX_MPDU_CMD handler 2278286441Srpaulo * 2279286441Srpaulo * Handles the actual data of the Rx packet from the fw 2280286441Srpaulo */ 2281286441Srpaulostatic void 2282286441Srpauloiwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, 2283286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2284286441Srpaulo{ 2285287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2286286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2287286441Srpaulo struct ieee80211_frame *wh; 2288286441Srpaulo struct ieee80211_node *ni; 2289286441Srpaulo struct ieee80211_rx_stats rxs; 2290286441Srpaulo struct mbuf *m; 2291286441Srpaulo struct iwm_rx_phy_info *phy_info; 2292286441Srpaulo struct iwm_rx_mpdu_res_start *rx_res; 2293286441Srpaulo uint32_t len; 2294286441Srpaulo uint32_t rx_pkt_status; 2295286441Srpaulo int rssi; 2296286441Srpaulo 2297286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2298286441Srpaulo 2299286441Srpaulo phy_info = &sc->sc_last_phy_info; 2300286441Srpaulo rx_res = (struct iwm_rx_mpdu_res_start *)pkt->data; 2301286441Srpaulo wh = (struct ieee80211_frame *)(pkt->data + sizeof(*rx_res)); 2302286441Srpaulo len = le16toh(rx_res->byte_count); 2303286441Srpaulo rx_pkt_status = le32toh(*(uint32_t *)(pkt->data + sizeof(*rx_res) + len)); 2304286441Srpaulo 2305286441Srpaulo m = data->m; 2306286441Srpaulo m->m_data = pkt->data + sizeof(*rx_res); 2307286441Srpaulo m->m_pkthdr.len = m->m_len = len; 2308286441Srpaulo 2309286441Srpaulo if (__predict_false(phy_info->cfg_phy_cnt > 20)) { 2310286441Srpaulo device_printf(sc->sc_dev, 2311286441Srpaulo "dsp size out of range [0,20]: %d\n", 2312286441Srpaulo phy_info->cfg_phy_cnt); 2313286441Srpaulo return; 2314286441Srpaulo } 2315286441Srpaulo 2316286441Srpaulo if (!(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_CRC_OK) || 2317286441Srpaulo !(rx_pkt_status & IWM_RX_MPDU_RES_STATUS_OVERRUN_OK)) { 2318286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2319286441Srpaulo "Bad CRC or FIFO: 0x%08X.\n", rx_pkt_status); 2320286441Srpaulo return; /* drop */ 2321286441Srpaulo } 2322286441Srpaulo 2323286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_RX_ENERGY_API) { 2324286441Srpaulo rssi = iwm_mvm_get_signal_strength(sc, phy_info); 2325286441Srpaulo } else { 2326286441Srpaulo rssi = iwm_mvm_calc_rssi(sc, phy_info); 2327286441Srpaulo } 2328286441Srpaulo rssi = (0 - IWM_MIN_DBM) + rssi; /* normalize */ 2329286441Srpaulo rssi = MIN(rssi, sc->sc_max_rssi); /* clip to max. 100% */ 2330286441Srpaulo 2331286441Srpaulo /* replenish ring for the buffer we're going to feed to the sharks */ 2332286441Srpaulo if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) { 2333286441Srpaulo device_printf(sc->sc_dev, "%s: unable to add more buffers\n", 2334286441Srpaulo __func__); 2335286441Srpaulo return; 2336286441Srpaulo } 2337286441Srpaulo 2338286441Srpaulo ni = ieee80211_find_rxnode(ic, (struct ieee80211_frame_min *)wh); 2339286441Srpaulo 2340286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, 2341286441Srpaulo "%s: phy_info: channel=%d, flags=0x%08x\n", 2342286441Srpaulo __func__, 2343286441Srpaulo le16toh(phy_info->channel), 2344286441Srpaulo le16toh(phy_info->phy_flags)); 2345286441Srpaulo 2346286441Srpaulo /* 2347286441Srpaulo * Populate an RX state struct with the provided information. 2348286441Srpaulo */ 2349286441Srpaulo bzero(&rxs, sizeof(rxs)); 2350286441Srpaulo rxs.r_flags |= IEEE80211_R_IEEE | IEEE80211_R_FREQ; 2351286441Srpaulo rxs.r_flags |= IEEE80211_R_NF | IEEE80211_R_RSSI; 2352286441Srpaulo rxs.c_ieee = le16toh(phy_info->channel); 2353286441Srpaulo if (le16toh(phy_info->phy_flags & IWM_RX_RES_PHY_FLAGS_BAND_24)) { 2354286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_2GHZ); 2355286441Srpaulo } else { 2356286441Srpaulo rxs.c_freq = ieee80211_ieee2mhz(rxs.c_ieee, IEEE80211_CHAN_5GHZ); 2357286441Srpaulo } 2358286441Srpaulo rxs.rssi = rssi - sc->sc_noise; 2359286441Srpaulo rxs.nf = sc->sc_noise; 2360286441Srpaulo 2361286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 2362286441Srpaulo struct iwm_rx_radiotap_header *tap = &sc->sc_rxtap; 2363286441Srpaulo 2364286441Srpaulo tap->wr_flags = 0; 2365286441Srpaulo if (phy_info->phy_flags & htole16(IWM_PHY_INFO_FLAG_SHPREAMBLE)) 2366286441Srpaulo tap->wr_flags |= IEEE80211_RADIOTAP_F_SHORTPRE; 2367286441Srpaulo tap->wr_chan_freq = htole16(rxs.c_freq); 2368286441Srpaulo /* XXX only if ic->ic_curchan->ic_ieee == rxs.c_ieee */ 2369286441Srpaulo tap->wr_chan_flags = htole16(ic->ic_curchan->ic_flags); 2370286441Srpaulo tap->wr_dbm_antsignal = (int8_t)rssi; 2371286441Srpaulo tap->wr_dbm_antnoise = (int8_t)sc->sc_noise; 2372286441Srpaulo tap->wr_tsft = phy_info->system_timestamp; 2373286441Srpaulo switch (phy_info->rate) { 2374286441Srpaulo /* CCK rates. */ 2375286441Srpaulo case 10: tap->wr_rate = 2; break; 2376286441Srpaulo case 20: tap->wr_rate = 4; break; 2377286441Srpaulo case 55: tap->wr_rate = 11; break; 2378286441Srpaulo case 110: tap->wr_rate = 22; break; 2379286441Srpaulo /* OFDM rates. */ 2380286441Srpaulo case 0xd: tap->wr_rate = 12; break; 2381286441Srpaulo case 0xf: tap->wr_rate = 18; break; 2382286441Srpaulo case 0x5: tap->wr_rate = 24; break; 2383286441Srpaulo case 0x7: tap->wr_rate = 36; break; 2384286441Srpaulo case 0x9: tap->wr_rate = 48; break; 2385286441Srpaulo case 0xb: tap->wr_rate = 72; break; 2386286441Srpaulo case 0x1: tap->wr_rate = 96; break; 2387286441Srpaulo case 0x3: tap->wr_rate = 108; break; 2388286441Srpaulo /* Unknown rate: should not happen. */ 2389286441Srpaulo default: tap->wr_rate = 0; 2390286441Srpaulo } 2391286441Srpaulo } 2392286441Srpaulo 2393286441Srpaulo IWM_UNLOCK(sc); 2394286441Srpaulo if (ni != NULL) { 2395286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "input m %p\n", m); 2396286441Srpaulo ieee80211_input_mimo(ni, m, &rxs); 2397286441Srpaulo ieee80211_free_node(ni); 2398286441Srpaulo } else { 2399286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RECV, "inputall m %p\n", m); 2400286441Srpaulo ieee80211_input_mimo_all(ic, m, &rxs); 2401286441Srpaulo } 2402286441Srpaulo IWM_LOCK(sc); 2403286441Srpaulo} 2404286441Srpaulo 2405286441Srpaulostatic void 2406286441Srpauloiwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, 2407286441Srpaulo struct iwm_node *in) 2408286441Srpaulo{ 2409286441Srpaulo struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; 2410287197Sglebius struct ieee80211vap *vap = in->in_ni.ni_vap; 2411286441Srpaulo int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; 2412286441Srpaulo int failack = tx_resp->failure_frame; 2413286441Srpaulo 2414286441Srpaulo KASSERT(tx_resp->frame_count == 1, ("too many frames")); 2415286441Srpaulo 2416286441Srpaulo /* Update rate control statistics. */ 2417286441Srpaulo if (status != IWM_TX_STATUS_SUCCESS && 2418286441Srpaulo status != IWM_TX_STATUS_DIRECT_DONE) { 2419287197Sglebius if_inc_counter(vap->iv_ifp, IFCOUNTER_OERRORS, 1); 2420287197Sglebius ieee80211_ratectl_tx_complete(vap, &in->in_ni, 2421286441Srpaulo IEEE80211_RATECTL_TX_FAILURE, &failack, NULL); 2422286441Srpaulo } else { 2423287197Sglebius if_inc_counter(vap->iv_ifp, IFCOUNTER_OPACKETS, 1); 2424287197Sglebius ieee80211_ratectl_tx_complete(vap, &in->in_ni, 2425286441Srpaulo IEEE80211_RATECTL_TX_SUCCESS, &failack, NULL); 2426286441Srpaulo 2427286441Srpaulo } 2428286441Srpaulo} 2429286441Srpaulo 2430286441Srpaulostatic void 2431286441Srpauloiwm_mvm_rx_tx_cmd(struct iwm_softc *sc, 2432286441Srpaulo struct iwm_rx_packet *pkt, struct iwm_rx_data *data) 2433286441Srpaulo{ 2434286441Srpaulo struct iwm_cmd_header *cmd_hdr = &pkt->hdr; 2435286441Srpaulo int idx = cmd_hdr->idx; 2436286441Srpaulo int qid = cmd_hdr->qid; 2437286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[qid]; 2438286441Srpaulo struct iwm_tx_data *txd = &ring->data[idx]; 2439286441Srpaulo struct iwm_node *in = txd->in; 2440286441Srpaulo 2441286441Srpaulo if (txd->done) { 2442286441Srpaulo device_printf(sc->sc_dev, 2443286441Srpaulo "%s: got tx interrupt that's already been handled!\n", 2444286441Srpaulo __func__); 2445286441Srpaulo return; 2446286441Srpaulo } 2447286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD); 2448286441Srpaulo 2449286441Srpaulo sc->sc_tx_timer = 0; 2450286441Srpaulo 2451286441Srpaulo iwm_mvm_rx_tx_cmd_single(sc, pkt, in); 2452286441Srpaulo 2453286441Srpaulo /* Unmap and free mbuf. */ 2454286441Srpaulo bus_dmamap_sync(ring->data_dmat, txd->map, BUS_DMASYNC_POSTWRITE); 2455286441Srpaulo bus_dmamap_unload(ring->data_dmat, txd->map); 2456286441Srpaulo m_freem(txd->m); 2457286441Srpaulo 2458286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 2459286441Srpaulo "free txd %p, in %p\n", txd, txd->in); 2460286441Srpaulo KASSERT(txd->done == 0, ("txd not done")); 2461286441Srpaulo txd->done = 1; 2462286441Srpaulo KASSERT(txd->in, ("txd without node")); 2463286441Srpaulo 2464286441Srpaulo txd->m = NULL; 2465286441Srpaulo txd->in = NULL; 2466286441Srpaulo ieee80211_free_node((struct ieee80211_node *)in); 2467286441Srpaulo 2468286441Srpaulo if (--ring->queued < IWM_TX_RING_LOMARK) { 2469286441Srpaulo sc->qfullmsk &= ~(1 << ring->qid); 2470287197Sglebius if (sc->qfullmsk == 0) { 2471286441Srpaulo /* 2472286441Srpaulo * Well, we're in interrupt context, but then again 2473286441Srpaulo * I guess net80211 does all sorts of stunts in 2474286441Srpaulo * interrupt context, so maybe this is no biggie. 2475286441Srpaulo */ 2476287197Sglebius iwm_start(sc); 2477286441Srpaulo } 2478286441Srpaulo } 2479286441Srpaulo} 2480286441Srpaulo 2481286441Srpaulo/* 2482286441Srpaulo * transmit side 2483286441Srpaulo */ 2484286441Srpaulo 2485286441Srpaulo/* 2486286441Srpaulo * Process a "command done" firmware notification. This is where we wakeup 2487286441Srpaulo * processes waiting for a synchronous command completion. 2488286441Srpaulo * from if_iwn 2489286441Srpaulo */ 2490286441Srpaulostatic void 2491286441Srpauloiwm_cmd_done(struct iwm_softc *sc, struct iwm_rx_packet *pkt) 2492286441Srpaulo{ 2493286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; 2494286441Srpaulo struct iwm_tx_data *data; 2495286441Srpaulo 2496286441Srpaulo if (pkt->hdr.qid != IWM_MVM_CMD_QUEUE) { 2497286441Srpaulo return; /* Not a command ack. */ 2498286441Srpaulo } 2499286441Srpaulo 2500286441Srpaulo data = &ring->data[pkt->hdr.idx]; 2501286441Srpaulo 2502286441Srpaulo /* If the command was mapped in an mbuf, free it. */ 2503286441Srpaulo if (data->m != NULL) { 2504286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 2505286441Srpaulo BUS_DMASYNC_POSTWRITE); 2506286441Srpaulo bus_dmamap_unload(ring->data_dmat, data->map); 2507286441Srpaulo m_freem(data->m); 2508286441Srpaulo data->m = NULL; 2509286441Srpaulo } 2510286441Srpaulo wakeup(&ring->desc[pkt->hdr.idx]); 2511286441Srpaulo} 2512286441Srpaulo 2513286441Srpaulo#if 0 2514286441Srpaulo/* 2515286441Srpaulo * necessary only for block ack mode 2516286441Srpaulo */ 2517286441Srpaulovoid 2518286441Srpauloiwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, 2519286441Srpaulo uint16_t len) 2520286441Srpaulo{ 2521286441Srpaulo struct iwm_agn_scd_bc_tbl *scd_bc_tbl; 2522286441Srpaulo uint16_t w_val; 2523286441Srpaulo 2524286441Srpaulo scd_bc_tbl = sc->sched_dma.vaddr; 2525286441Srpaulo 2526286441Srpaulo len += 8; /* magic numbers came naturally from paris */ 2527286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_DW_BC_TABLE) 2528286441Srpaulo len = roundup(len, 4) / 4; 2529286441Srpaulo 2530286441Srpaulo w_val = htole16(sta_id << 12 | len); 2531286441Srpaulo 2532286441Srpaulo /* Update TX scheduler. */ 2533286441Srpaulo scd_bc_tbl[qid].tfd_offset[idx] = w_val; 2534286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 2535286441Srpaulo BUS_DMASYNC_PREWRITE); 2536286441Srpaulo 2537286441Srpaulo /* I really wonder what this is ?!? */ 2538286441Srpaulo if (idx < IWM_TFD_QUEUE_SIZE_BC_DUP) { 2539286441Srpaulo scd_bc_tbl[qid].tfd_offset[IWM_TFD_QUEUE_SIZE_MAX + idx] = w_val; 2540286441Srpaulo bus_dmamap_sync(sc->sched_dma.tag, sc->sched_dma.map, 2541286441Srpaulo BUS_DMASYNC_PREWRITE); 2542286441Srpaulo } 2543286441Srpaulo} 2544286441Srpaulo#endif 2545286441Srpaulo 2546286441Srpaulo/* 2547286441Srpaulo * Take an 802.11 (non-n) rate, find the relevant rate 2548286441Srpaulo * table entry. return the index into in_ridx[]. 2549286441Srpaulo * 2550286441Srpaulo * The caller then uses that index back into in_ridx 2551286441Srpaulo * to figure out the rate index programmed /into/ 2552286441Srpaulo * the firmware for this given node. 2553286441Srpaulo */ 2554286441Srpaulostatic int 2555286441Srpauloiwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, 2556286441Srpaulo uint8_t rate) 2557286441Srpaulo{ 2558286441Srpaulo int i; 2559286441Srpaulo uint8_t r; 2560286441Srpaulo 2561286441Srpaulo for (i = 0; i < nitems(in->in_ridx); i++) { 2562286441Srpaulo r = iwm_rates[in->in_ridx[i]].rate; 2563286441Srpaulo if (rate == r) 2564286441Srpaulo return (i); 2565286441Srpaulo } 2566286441Srpaulo /* XXX Return the first */ 2567286441Srpaulo /* XXX TODO: have it return the /lowest/ */ 2568286441Srpaulo return (0); 2569286441Srpaulo} 2570286441Srpaulo 2571286441Srpaulo/* 2572286441Srpaulo * Fill in various bit for management frames, and leave them 2573286441Srpaulo * unfilled for data frames (firmware takes care of that). 2574286441Srpaulo * Return the selected TX rate. 2575286441Srpaulo */ 2576286441Srpaulostatic const struct iwm_rate * 2577286441Srpauloiwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, 2578286441Srpaulo struct ieee80211_frame *wh, struct iwm_tx_cmd *tx) 2579286441Srpaulo{ 2580287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2581286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 2582286441Srpaulo const struct iwm_rate *rinfo; 2583286441Srpaulo int type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 2584286441Srpaulo int ridx, rate_flags; 2585286441Srpaulo 2586286441Srpaulo tx->rts_retry_limit = IWM_RTS_DFAULT_RETRY_LIMIT; 2587286441Srpaulo tx->data_retry_limit = IWM_DEFAULT_TX_RETRY; 2588286441Srpaulo 2589286441Srpaulo /* 2590286441Srpaulo * XXX TODO: everything about the rate selection here is terrible! 2591286441Srpaulo */ 2592286441Srpaulo 2593286441Srpaulo if (type == IEEE80211_FC0_TYPE_DATA) { 2594286441Srpaulo int i; 2595286441Srpaulo /* for data frames, use RS table */ 2596286441Srpaulo (void) ieee80211_ratectl_rate(ni, NULL, 0); 2597286441Srpaulo i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); 2598286441Srpaulo ridx = in->in_ridx[i]; 2599286441Srpaulo 2600286441Srpaulo /* This is the index into the programmed table */ 2601286441Srpaulo tx->initial_rate_index = i; 2602286441Srpaulo tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); 2603286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, 2604286441Srpaulo "%s: start with i=%d, txrate %d\n", 2605286441Srpaulo __func__, i, iwm_rates[ridx].rate); 2606286441Srpaulo /* XXX no rate_n_flags? */ 2607286441Srpaulo return &iwm_rates[ridx]; 2608286441Srpaulo } 2609286441Srpaulo 2610286441Srpaulo /* 2611286441Srpaulo * For non-data, use the lowest supported rate for the given 2612286441Srpaulo * operational mode. 2613286441Srpaulo * 2614286441Srpaulo * Note: there may not be any rate control information available. 2615286441Srpaulo * This driver currently assumes if we're transmitting data 2616286441Srpaulo * frames, use the rate control table. Grr. 2617286441Srpaulo * 2618286441Srpaulo * XXX TODO: use the configured rate for the traffic type! 2619286441Srpaulo */ 2620286441Srpaulo if (ic->ic_curmode == IEEE80211_MODE_11A) { 2621286441Srpaulo /* 2622286441Srpaulo * XXX this assumes the mode is either 11a or not 11a; 2623286441Srpaulo * definitely won't work for 11n. 2624286441Srpaulo */ 2625286441Srpaulo ridx = IWM_RIDX_OFDM; 2626286441Srpaulo } else { 2627286441Srpaulo ridx = IWM_RIDX_CCK; 2628286441Srpaulo } 2629286441Srpaulo 2630286441Srpaulo rinfo = &iwm_rates[ridx]; 2631286441Srpaulo 2632286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: ridx=%d; rate=%d, CCK=%d\n", 2633286441Srpaulo __func__, ridx, 2634286441Srpaulo rinfo->rate, 2635286441Srpaulo !! (IWM_RIDX_IS_CCK(ridx)) 2636286441Srpaulo ); 2637286441Srpaulo 2638286441Srpaulo /* XXX TODO: hard-coded TX antenna? */ 2639286441Srpaulo rate_flags = 1 << IWM_RATE_MCS_ANT_POS; 2640286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 2641286441Srpaulo rate_flags |= IWM_RATE_MCS_CCK_MSK; 2642286441Srpaulo /* XXX hard-coded tx rate */ 2643286441Srpaulo tx->rate_n_flags = htole32(rate_flags | rinfo->plcp); 2644286441Srpaulo 2645286441Srpaulo return rinfo; 2646286441Srpaulo} 2647286441Srpaulo 2648286441Srpaulo#define TB0_SIZE 16 2649286441Srpaulostatic int 2650286441Srpauloiwm_tx(struct iwm_softc *sc, struct mbuf *m, struct ieee80211_node *ni, int ac) 2651286441Srpaulo{ 2652287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 2653286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 2654286441Srpaulo struct iwm_node *in = (struct iwm_node *)ni; 2655286441Srpaulo struct iwm_tx_ring *ring; 2656286441Srpaulo struct iwm_tx_data *data; 2657286441Srpaulo struct iwm_tfd *desc; 2658286441Srpaulo struct iwm_device_cmd *cmd; 2659286441Srpaulo struct iwm_tx_cmd *tx; 2660286441Srpaulo struct ieee80211_frame *wh; 2661286441Srpaulo struct ieee80211_key *k = NULL; 2662286441Srpaulo struct mbuf *m1; 2663286441Srpaulo const struct iwm_rate *rinfo; 2664286441Srpaulo uint32_t flags; 2665286441Srpaulo u_int hdrlen; 2666286441Srpaulo bus_dma_segment_t *seg, segs[IWM_MAX_SCATTER]; 2667286441Srpaulo int nsegs; 2668286441Srpaulo uint8_t tid, type; 2669286441Srpaulo int i, totlen, error, pad; 2670286441Srpaulo 2671286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 2672286441Srpaulo hdrlen = ieee80211_anyhdrsize(wh); 2673286441Srpaulo type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; 2674286441Srpaulo tid = 0; 2675286441Srpaulo ring = &sc->txq[ac]; 2676286441Srpaulo desc = &ring->desc[ring->cur]; 2677286441Srpaulo memset(desc, 0, sizeof(*desc)); 2678286441Srpaulo data = &ring->data[ring->cur]; 2679286441Srpaulo 2680286441Srpaulo /* Fill out iwm_tx_cmd to send to the firmware */ 2681286441Srpaulo cmd = &ring->cmd[ring->cur]; 2682286441Srpaulo cmd->hdr.code = IWM_TX_CMD; 2683286441Srpaulo cmd->hdr.flags = 0; 2684286441Srpaulo cmd->hdr.qid = ring->qid; 2685286441Srpaulo cmd->hdr.idx = ring->cur; 2686286441Srpaulo 2687286441Srpaulo tx = (void *)cmd->data; 2688286441Srpaulo memset(tx, 0, sizeof(*tx)); 2689286441Srpaulo 2690286441Srpaulo rinfo = iwm_tx_fill_cmd(sc, in, wh, tx); 2691286441Srpaulo 2692286441Srpaulo /* Encrypt the frame if need be. */ 2693286441Srpaulo if (wh->i_fc[1] & IEEE80211_FC1_PROTECTED) { 2694286441Srpaulo /* Retrieve key for TX && do software encryption. */ 2695286441Srpaulo k = ieee80211_crypto_encap(ni, m); 2696286441Srpaulo if (k == NULL) { 2697286441Srpaulo m_freem(m); 2698286441Srpaulo return (ENOBUFS); 2699286441Srpaulo } 2700286441Srpaulo /* 802.11 header may have moved. */ 2701286441Srpaulo wh = mtod(m, struct ieee80211_frame *); 2702286441Srpaulo } 2703286441Srpaulo 2704286441Srpaulo if (ieee80211_radiotap_active_vap(vap)) { 2705286441Srpaulo struct iwm_tx_radiotap_header *tap = &sc->sc_txtap; 2706286441Srpaulo 2707286441Srpaulo tap->wt_flags = 0; 2708286441Srpaulo tap->wt_chan_freq = htole16(ni->ni_chan->ic_freq); 2709286441Srpaulo tap->wt_chan_flags = htole16(ni->ni_chan->ic_flags); 2710286441Srpaulo tap->wt_rate = rinfo->rate; 2711286441Srpaulo tap->wt_hwqueue = ac; 2712286441Srpaulo if (k != NULL) 2713286441Srpaulo tap->wt_flags |= IEEE80211_RADIOTAP_F_WEP; 2714286441Srpaulo ieee80211_radiotap_tx(vap, m); 2715286441Srpaulo } 2716286441Srpaulo 2717286441Srpaulo 2718286441Srpaulo totlen = m->m_pkthdr.len; 2719286441Srpaulo 2720286441Srpaulo flags = 0; 2721286441Srpaulo if (!IEEE80211_IS_MULTICAST(wh->i_addr1)) { 2722286441Srpaulo flags |= IWM_TX_CMD_FLG_ACK; 2723286441Srpaulo } 2724286441Srpaulo 2725286441Srpaulo if (type != IEEE80211_FC0_TYPE_DATA 2726286441Srpaulo && (totlen + IEEE80211_CRC_LEN > vap->iv_rtsthreshold) 2727286441Srpaulo && !IEEE80211_IS_MULTICAST(wh->i_addr1)) { 2728286441Srpaulo flags |= IWM_TX_CMD_FLG_PROT_REQUIRE; 2729286441Srpaulo } 2730286441Srpaulo 2731286441Srpaulo if (IEEE80211_IS_MULTICAST(wh->i_addr1) || 2732286441Srpaulo type != IEEE80211_FC0_TYPE_DATA) 2733286441Srpaulo tx->sta_id = sc->sc_aux_sta.sta_id; 2734286441Srpaulo else 2735286441Srpaulo tx->sta_id = IWM_STATION_ID; 2736286441Srpaulo 2737286441Srpaulo if (type == IEEE80211_FC0_TYPE_MGT) { 2738286441Srpaulo uint8_t subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; 2739286441Srpaulo 2740286441Srpaulo if (subtype == IEEE80211_FC0_SUBTYPE_ASSOC_REQ || 2741286441Srpaulo subtype == IEEE80211_FC0_SUBTYPE_REASSOC_REQ) 2742286441Srpaulo tx->pm_frame_timeout = htole16(3); 2743286441Srpaulo else 2744286441Srpaulo tx->pm_frame_timeout = htole16(2); 2745286441Srpaulo } else { 2746286441Srpaulo tx->pm_frame_timeout = htole16(0); 2747286441Srpaulo } 2748286441Srpaulo 2749286441Srpaulo if (hdrlen & 3) { 2750286441Srpaulo /* First segment length must be a multiple of 4. */ 2751286441Srpaulo flags |= IWM_TX_CMD_FLG_MH_PAD; 2752286441Srpaulo pad = 4 - (hdrlen & 3); 2753286441Srpaulo } else 2754286441Srpaulo pad = 0; 2755286441Srpaulo 2756286441Srpaulo tx->driver_txop = 0; 2757286441Srpaulo tx->next_frame_len = 0; 2758286441Srpaulo 2759286441Srpaulo tx->len = htole16(totlen); 2760286441Srpaulo tx->tid_tspec = tid; 2761286441Srpaulo tx->life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); 2762286441Srpaulo 2763286441Srpaulo /* Set physical address of "scratch area". */ 2764286441Srpaulo tx->dram_lsb_ptr = htole32(data->scratch_paddr); 2765286441Srpaulo tx->dram_msb_ptr = iwm_get_dma_hi_addr(data->scratch_paddr); 2766286441Srpaulo 2767286441Srpaulo /* Copy 802.11 header in TX command. */ 2768286441Srpaulo memcpy(((uint8_t *)tx) + sizeof(*tx), wh, hdrlen); 2769286441Srpaulo 2770286441Srpaulo flags |= IWM_TX_CMD_FLG_BT_DIS | IWM_TX_CMD_FLG_SEQ_CTL; 2771286441Srpaulo 2772286441Srpaulo tx->sec_ctl = 0; 2773286441Srpaulo tx->tx_flags |= htole32(flags); 2774286441Srpaulo 2775286441Srpaulo /* Trim 802.11 header. */ 2776286441Srpaulo m_adj(m, hdrlen); 2777286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 2778286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 2779286441Srpaulo if (error != 0) { 2780286441Srpaulo if (error != EFBIG) { 2781286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 2782286441Srpaulo error); 2783286441Srpaulo m_freem(m); 2784286441Srpaulo return error; 2785286441Srpaulo } 2786286441Srpaulo /* Too many DMA segments, linearize mbuf. */ 2787286441Srpaulo MGETHDR(m1, M_NOWAIT, MT_DATA); 2788286441Srpaulo if (m1 == NULL) { 2789286441Srpaulo m_freem(m); 2790286441Srpaulo return ENOBUFS; 2791286441Srpaulo } 2792286441Srpaulo if (m->m_pkthdr.len > MHLEN) { 2793286441Srpaulo MCLGET(m1, M_NOWAIT); 2794286441Srpaulo if (!(m1->m_flags & M_EXT)) { 2795286441Srpaulo m_freem(m); 2796286441Srpaulo m_freem(m1); 2797286441Srpaulo return ENOBUFS; 2798286441Srpaulo } 2799286441Srpaulo } 2800286441Srpaulo m_copydata(m, 0, m->m_pkthdr.len, mtod(m1, void *)); 2801286441Srpaulo m1->m_pkthdr.len = m1->m_len = m->m_pkthdr.len; 2802286441Srpaulo m_freem(m); 2803286441Srpaulo m = m1; 2804286441Srpaulo error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, 2805286441Srpaulo segs, &nsegs, BUS_DMA_NOWAIT); 2806286441Srpaulo if (error != 0) { 2807286441Srpaulo device_printf(sc->sc_dev, "can't map mbuf (error %d)\n", 2808286441Srpaulo error); 2809286441Srpaulo m_freem(m); 2810286441Srpaulo return error; 2811286441Srpaulo } 2812286441Srpaulo } 2813286441Srpaulo data->m = m; 2814286441Srpaulo data->in = in; 2815286441Srpaulo data->done = 0; 2816286441Srpaulo 2817286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 2818286441Srpaulo "sending txd %p, in %p\n", data, data->in); 2819286441Srpaulo KASSERT(data->in != NULL, ("node is NULL")); 2820286441Srpaulo 2821286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 2822286441Srpaulo "sending data: qid=%d idx=%d len=%d nsegs=%d\n", 2823286441Srpaulo ring->qid, ring->cur, totlen, nsegs); 2824286441Srpaulo 2825286441Srpaulo /* Fill TX descriptor. */ 2826286441Srpaulo desc->num_tbs = 2 + nsegs; 2827286441Srpaulo 2828286441Srpaulo desc->tbs[0].lo = htole32(data->cmd_paddr); 2829286441Srpaulo desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 2830286441Srpaulo (TB0_SIZE << 4); 2831286441Srpaulo desc->tbs[1].lo = htole32(data->cmd_paddr + TB0_SIZE); 2832286441Srpaulo desc->tbs[1].hi_n_len = htole16(iwm_get_dma_hi_addr(data->cmd_paddr)) | 2833286441Srpaulo ((sizeof(struct iwm_cmd_header) + sizeof(*tx) 2834286441Srpaulo + hdrlen + pad - TB0_SIZE) << 4); 2835286441Srpaulo 2836286441Srpaulo /* Other DMA segments are for data payload. */ 2837286441Srpaulo for (i = 0; i < nsegs; i++) { 2838286441Srpaulo seg = &segs[i]; 2839286441Srpaulo desc->tbs[i+2].lo = htole32(seg->ds_addr); 2840286441Srpaulo desc->tbs[i+2].hi_n_len = \ 2841286441Srpaulo htole16(iwm_get_dma_hi_addr(seg->ds_addr)) 2842286441Srpaulo | ((seg->ds_len) << 4); 2843286441Srpaulo } 2844286441Srpaulo 2845286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, 2846286441Srpaulo BUS_DMASYNC_PREWRITE); 2847286441Srpaulo bus_dmamap_sync(ring->cmd_dma.tag, ring->cmd_dma.map, 2848286441Srpaulo BUS_DMASYNC_PREWRITE); 2849286441Srpaulo bus_dmamap_sync(ring->desc_dma.tag, ring->desc_dma.map, 2850286441Srpaulo BUS_DMASYNC_PREWRITE); 2851286441Srpaulo 2852286441Srpaulo#if 0 2853286441Srpaulo iwm_update_sched(sc, ring->qid, ring->cur, tx->sta_id, le16toh(tx->len)); 2854286441Srpaulo#endif 2855286441Srpaulo 2856286441Srpaulo /* Kick TX ring. */ 2857286441Srpaulo ring->cur = (ring->cur + 1) % IWM_TX_RING_COUNT; 2858286441Srpaulo IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, ring->qid << 8 | ring->cur); 2859286441Srpaulo 2860286441Srpaulo /* Mark TX ring as full if we reach a certain threshold. */ 2861286441Srpaulo if (++ring->queued > IWM_TX_RING_HIMARK) { 2862286441Srpaulo sc->qfullmsk |= 1 << ring->qid; 2863286441Srpaulo } 2864286441Srpaulo 2865286441Srpaulo return 0; 2866286441Srpaulo} 2867286441Srpaulo 2868286441Srpaulostatic int 2869286441Srpauloiwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, 2870286441Srpaulo const struct ieee80211_bpf_params *params) 2871286441Srpaulo{ 2872286441Srpaulo struct ieee80211com *ic = ni->ni_ic; 2873286865Sadrian struct iwm_softc *sc = ic->ic_softc; 2874286441Srpaulo int error = 0; 2875286441Srpaulo 2876286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 2877286441Srpaulo "->%s begin\n", __func__); 2878286441Srpaulo 2879287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 2880286441Srpaulo ieee80211_free_node(ni); 2881286441Srpaulo m_freem(m); 2882286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT, 2883286441Srpaulo "<-%s not RUNNING\n", __func__); 2884286441Srpaulo return (ENETDOWN); 2885286441Srpaulo } 2886286441Srpaulo 2887286441Srpaulo IWM_LOCK(sc); 2888286441Srpaulo /* XXX fix this */ 2889286441Srpaulo if (params == NULL) { 2890286441Srpaulo error = iwm_tx(sc, m, ni, 0); 2891286441Srpaulo } else { 2892286441Srpaulo error = iwm_tx(sc, m, ni, 0); 2893286441Srpaulo } 2894286441Srpaulo if (error != 0) { 2895286441Srpaulo /* NB: m is reclaimed on tx failure */ 2896286441Srpaulo ieee80211_free_node(ni); 2897286441Srpaulo } 2898286441Srpaulo sc->sc_tx_timer = 5; 2899286441Srpaulo IWM_UNLOCK(sc); 2900286441Srpaulo 2901286441Srpaulo return (error); 2902286441Srpaulo} 2903286441Srpaulo 2904286441Srpaulo/* 2905286441Srpaulo * mvm/tx.c 2906286441Srpaulo */ 2907286441Srpaulo 2908286441Srpaulo#if 0 2909286441Srpaulo/* 2910286441Srpaulo * Note that there are transports that buffer frames before they reach 2911286441Srpaulo * the firmware. This means that after flush_tx_path is called, the 2912286441Srpaulo * queue might not be empty. The race-free way to handle this is to: 2913286441Srpaulo * 1) set the station as draining 2914286441Srpaulo * 2) flush the Tx path 2915286441Srpaulo * 3) wait for the transport queues to be empty 2916286441Srpaulo */ 2917286441Srpauloint 2918286441Srpauloiwm_mvm_flush_tx_path(struct iwm_softc *sc, int tfd_msk, int sync) 2919286441Srpaulo{ 2920286441Srpaulo struct iwm_tx_path_flush_cmd flush_cmd = { 2921286441Srpaulo .queues_ctl = htole32(tfd_msk), 2922286441Srpaulo .flush_ctl = htole16(IWM_DUMP_TX_FIFO_FLUSH), 2923286441Srpaulo }; 2924286441Srpaulo int ret; 2925286441Srpaulo 2926286441Srpaulo ret = iwm_mvm_send_cmd_pdu(sc, IWM_TXPATH_FLUSH, 2927286441Srpaulo sync ? IWM_CMD_SYNC : IWM_CMD_ASYNC, 2928286441Srpaulo sizeof(flush_cmd), &flush_cmd); 2929286441Srpaulo if (ret) 2930286441Srpaulo device_printf(sc->sc_dev, 2931286441Srpaulo "Flushing tx queue failed: %d\n", ret); 2932286441Srpaulo return ret; 2933286441Srpaulo} 2934286441Srpaulo#endif 2935286441Srpaulo 2936286441Srpaulo/* 2937286441Srpaulo * BEGIN mvm/sta.c 2938286441Srpaulo */ 2939286441Srpaulo 2940286441Srpaulostatic void 2941286441Srpauloiwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6, 2942286441Srpaulo struct iwm_mvm_add_sta_cmd_v5 *cmd_v5) 2943286441Srpaulo{ 2944286441Srpaulo memset(cmd_v5, 0, sizeof(*cmd_v5)); 2945286441Srpaulo 2946286441Srpaulo cmd_v5->add_modify = cmd_v6->add_modify; 2947286441Srpaulo cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; 2948286441Srpaulo cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; 2949286441Srpaulo IEEE80211_ADDR_COPY(cmd_v5->addr, cmd_v6->addr); 2950286441Srpaulo cmd_v5->sta_id = cmd_v6->sta_id; 2951286441Srpaulo cmd_v5->modify_mask = cmd_v6->modify_mask; 2952286441Srpaulo cmd_v5->station_flags = cmd_v6->station_flags; 2953286441Srpaulo cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; 2954286441Srpaulo cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; 2955286441Srpaulo cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; 2956286441Srpaulo cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; 2957286441Srpaulo cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; 2958286441Srpaulo cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; 2959286441Srpaulo cmd_v5->assoc_id = cmd_v6->assoc_id; 2960286441Srpaulo cmd_v5->beamform_flags = cmd_v6->beamform_flags; 2961286441Srpaulo cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; 2962286441Srpaulo} 2963286441Srpaulo 2964286441Srpaulostatic int 2965286441Srpauloiwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, 2966286441Srpaulo struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status) 2967286441Srpaulo{ 2968286441Srpaulo struct iwm_mvm_add_sta_cmd_v5 cmd_v5; 2969286441Srpaulo 2970286441Srpaulo if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) { 2971286441Srpaulo return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, 2972286441Srpaulo sizeof(*cmd), cmd, status); 2973286441Srpaulo } 2974286441Srpaulo 2975286441Srpaulo iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); 2976286441Srpaulo 2977286441Srpaulo return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5), 2978286441Srpaulo &cmd_v5, status); 2979286441Srpaulo} 2980286441Srpaulo 2981286441Srpaulo/* send station add/update command to firmware */ 2982286441Srpaulostatic int 2983286441Srpauloiwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) 2984286441Srpaulo{ 2985286441Srpaulo struct iwm_mvm_add_sta_cmd_v6 add_sta_cmd; 2986286441Srpaulo int ret; 2987286441Srpaulo uint32_t status; 2988286441Srpaulo 2989286441Srpaulo memset(&add_sta_cmd, 0, sizeof(add_sta_cmd)); 2990286441Srpaulo 2991286441Srpaulo add_sta_cmd.sta_id = IWM_STATION_ID; 2992286441Srpaulo add_sta_cmd.mac_id_n_color 2993286441Srpaulo = htole32(IWM_FW_CMD_ID_AND_COLOR(IWM_DEFAULT_MACID, 2994286441Srpaulo IWM_DEFAULT_COLOR)); 2995286441Srpaulo if (!update) { 2996286441Srpaulo add_sta_cmd.tfd_queue_msk = htole32(0xf); 2997286441Srpaulo IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); 2998286441Srpaulo } 2999286441Srpaulo add_sta_cmd.add_modify = update ? 1 : 0; 3000286441Srpaulo add_sta_cmd.station_flags_msk 3001286441Srpaulo |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); 3002286441Srpaulo 3003286441Srpaulo status = IWM_ADD_STA_SUCCESS; 3004286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &add_sta_cmd, &status); 3005286441Srpaulo if (ret) 3006286441Srpaulo return ret; 3007286441Srpaulo 3008286441Srpaulo switch (status) { 3009286441Srpaulo case IWM_ADD_STA_SUCCESS: 3010286441Srpaulo break; 3011286441Srpaulo default: 3012286441Srpaulo ret = EIO; 3013286441Srpaulo device_printf(sc->sc_dev, "IWM_ADD_STA failed\n"); 3014286441Srpaulo break; 3015286441Srpaulo } 3016286441Srpaulo 3017286441Srpaulo return ret; 3018286441Srpaulo} 3019286441Srpaulo 3020286441Srpaulostatic int 3021286441Srpauloiwm_mvm_add_sta(struct iwm_softc *sc, struct iwm_node *in) 3022286441Srpaulo{ 3023286441Srpaulo int ret; 3024286441Srpaulo 3025286441Srpaulo ret = iwm_mvm_sta_send_to_fw(sc, in, 0); 3026286441Srpaulo if (ret) 3027286441Srpaulo return ret; 3028286441Srpaulo 3029286441Srpaulo return 0; 3030286441Srpaulo} 3031286441Srpaulo 3032286441Srpaulostatic int 3033286441Srpauloiwm_mvm_update_sta(struct iwm_softc *sc, struct iwm_node *in) 3034286441Srpaulo{ 3035286441Srpaulo return iwm_mvm_sta_send_to_fw(sc, in, 1); 3036286441Srpaulo} 3037286441Srpaulo 3038286441Srpaulostatic int 3039286441Srpauloiwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, 3040286441Srpaulo const uint8_t *addr, uint16_t mac_id, uint16_t color) 3041286441Srpaulo{ 3042286441Srpaulo struct iwm_mvm_add_sta_cmd_v6 cmd; 3043286441Srpaulo int ret; 3044286441Srpaulo uint32_t status; 3045286441Srpaulo 3046286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3047286441Srpaulo cmd.sta_id = sta->sta_id; 3048286441Srpaulo cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); 3049286441Srpaulo 3050286441Srpaulo cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); 3051286441Srpaulo 3052286441Srpaulo if (addr) 3053286441Srpaulo IEEE80211_ADDR_COPY(cmd.addr, addr); 3054286441Srpaulo 3055286441Srpaulo ret = iwm_mvm_send_add_sta_cmd_status(sc, &cmd, &status); 3056286441Srpaulo if (ret) 3057286441Srpaulo return ret; 3058286441Srpaulo 3059286441Srpaulo switch (status) { 3060286441Srpaulo case IWM_ADD_STA_SUCCESS: 3061286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 3062286441Srpaulo "%s: Internal station added.\n", __func__); 3063286441Srpaulo return 0; 3064286441Srpaulo default: 3065286441Srpaulo device_printf(sc->sc_dev, 3066286441Srpaulo "%s: Add internal station failed, status=0x%x\n", 3067286441Srpaulo __func__, status); 3068286441Srpaulo ret = EIO; 3069286441Srpaulo break; 3070286441Srpaulo } 3071286441Srpaulo return ret; 3072286441Srpaulo} 3073286441Srpaulo 3074286441Srpaulostatic int 3075286441Srpauloiwm_mvm_add_aux_sta(struct iwm_softc *sc) 3076286441Srpaulo{ 3077286441Srpaulo int ret; 3078286441Srpaulo 3079286441Srpaulo sc->sc_aux_sta.sta_id = 3; 3080286441Srpaulo sc->sc_aux_sta.tfd_queue_msk = 0; 3081286441Srpaulo 3082286441Srpaulo ret = iwm_mvm_add_int_sta_common(sc, 3083286441Srpaulo &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); 3084286441Srpaulo 3085286441Srpaulo if (ret) 3086286441Srpaulo memset(&sc->sc_aux_sta, 0, sizeof(sc->sc_aux_sta)); 3087286441Srpaulo return ret; 3088286441Srpaulo} 3089286441Srpaulo 3090286441Srpaulo/* 3091286441Srpaulo * END mvm/sta.c 3092286441Srpaulo */ 3093286441Srpaulo 3094286441Srpaulo/* 3095286441Srpaulo * BEGIN mvm/quota.c 3096286441Srpaulo */ 3097286441Srpaulo 3098286441Srpaulostatic int 3099286441Srpauloiwm_mvm_update_quotas(struct iwm_softc *sc, struct iwm_node *in) 3100286441Srpaulo{ 3101286441Srpaulo struct iwm_time_quota_cmd cmd; 3102286441Srpaulo int i, idx, ret, num_active_macs, quota, quota_rem; 3103286441Srpaulo int colors[IWM_MAX_BINDINGS] = { -1, -1, -1, -1, }; 3104286441Srpaulo int n_ifs[IWM_MAX_BINDINGS] = {0, }; 3105286441Srpaulo uint16_t id; 3106286441Srpaulo 3107286441Srpaulo memset(&cmd, 0, sizeof(cmd)); 3108286441Srpaulo 3109286441Srpaulo /* currently, PHY ID == binding ID */ 3110286441Srpaulo if (in) { 3111286441Srpaulo id = in->in_phyctxt->id; 3112286441Srpaulo KASSERT(id < IWM_MAX_BINDINGS, ("invalid id")); 3113286441Srpaulo colors[id] = in->in_phyctxt->color; 3114286441Srpaulo 3115286441Srpaulo if (1) 3116286441Srpaulo n_ifs[id] = 1; 3117286441Srpaulo } 3118286441Srpaulo 3119286441Srpaulo /* 3120286441Srpaulo * The FW's scheduling session consists of 3121286441Srpaulo * IWM_MVM_MAX_QUOTA fragments. Divide these fragments 3122286441Srpaulo * equally between all the bindings that require quota 3123286441Srpaulo */ 3124286441Srpaulo num_active_macs = 0; 3125286441Srpaulo for (i = 0; i < IWM_MAX_BINDINGS; i++) { 3126286441Srpaulo cmd.quotas[i].id_and_color = htole32(IWM_FW_CTXT_INVALID); 3127286441Srpaulo num_active_macs += n_ifs[i]; 3128286441Srpaulo } 3129286441Srpaulo 3130286441Srpaulo quota = 0; 3131286441Srpaulo quota_rem = 0; 3132286441Srpaulo if (num_active_macs) { 3133286441Srpaulo quota = IWM_MVM_MAX_QUOTA / num_active_macs; 3134286441Srpaulo quota_rem = IWM_MVM_MAX_QUOTA % num_active_macs; 3135286441Srpaulo } 3136286441Srpaulo 3137286441Srpaulo for (idx = 0, i = 0; i < IWM_MAX_BINDINGS; i++) { 3138286441Srpaulo if (colors[i] < 0) 3139286441Srpaulo continue; 3140286441Srpaulo 3141286441Srpaulo cmd.quotas[idx].id_and_color = 3142286441Srpaulo htole32(IWM_FW_CMD_ID_AND_COLOR(i, colors[i])); 3143286441Srpaulo 3144286441Srpaulo if (n_ifs[i] <= 0) { 3145286441Srpaulo cmd.quotas[idx].quota = htole32(0); 3146286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3147286441Srpaulo } else { 3148286441Srpaulo cmd.quotas[idx].quota = htole32(quota * n_ifs[i]); 3149286441Srpaulo cmd.quotas[idx].max_duration = htole32(0); 3150286441Srpaulo } 3151286441Srpaulo idx++; 3152286441Srpaulo } 3153286441Srpaulo 3154286441Srpaulo /* Give the remainder of the session to the first binding */ 3155286441Srpaulo cmd.quotas[0].quota = htole32(le32toh(cmd.quotas[0].quota) + quota_rem); 3156286441Srpaulo 3157286441Srpaulo ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_QUOTA_CMD, IWM_CMD_SYNC, 3158286441Srpaulo sizeof(cmd), &cmd); 3159286441Srpaulo if (ret) 3160286441Srpaulo device_printf(sc->sc_dev, 3161286441Srpaulo "%s: Failed to send quota: %d\n", __func__, ret); 3162286441Srpaulo return ret; 3163286441Srpaulo} 3164286441Srpaulo 3165286441Srpaulo/* 3166286441Srpaulo * END mvm/quota.c 3167286441Srpaulo */ 3168286441Srpaulo 3169286441Srpaulo/* 3170286441Srpaulo * ieee80211 routines 3171286441Srpaulo */ 3172286441Srpaulo 3173286441Srpaulo/* 3174286441Srpaulo * Change to AUTH state in 80211 state machine. Roughly matches what 3175286441Srpaulo * Linux does in bss_info_changed(). 3176286441Srpaulo */ 3177286441Srpaulostatic int 3178286441Srpauloiwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) 3179286441Srpaulo{ 3180286441Srpaulo struct ieee80211_node *ni; 3181286441Srpaulo struct iwm_node *in; 3182286441Srpaulo struct iwm_vap *iv = IWM_VAP(vap); 3183286441Srpaulo uint32_t duration; 3184286441Srpaulo uint32_t min_duration; 3185286441Srpaulo int error; 3186286441Srpaulo 3187286441Srpaulo /* 3188286441Srpaulo * XXX i have a feeling that the vap node is being 3189286441Srpaulo * freed from underneath us. Grr. 3190286441Srpaulo */ 3191286441Srpaulo ni = ieee80211_ref_node(vap->iv_bss); 3192286441Srpaulo in = (struct iwm_node *) ni; 3193286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_STATE, 3194286441Srpaulo "%s: called; vap=%p, bss ni=%p\n", 3195286441Srpaulo __func__, 3196286441Srpaulo vap, 3197286441Srpaulo ni); 3198286441Srpaulo 3199286441Srpaulo in->in_assoc = 0; 3200286441Srpaulo 3201286441Srpaulo error = iwm_allow_mcast(vap, sc); 3202286441Srpaulo if (error) { 3203286441Srpaulo device_printf(sc->sc_dev, 3204286441Srpaulo "%s: failed to set multicast\n", __func__); 3205286441Srpaulo goto out; 3206286441Srpaulo } 3207286441Srpaulo 3208286441Srpaulo /* 3209286441Srpaulo * This is where it deviates from what Linux does. 3210286441Srpaulo * 3211286441Srpaulo * Linux iwlwifi doesn't reset the nic each time, nor does it 3212286441Srpaulo * call ctxt_add() here. Instead, it adds it during vap creation, 3213286441Srpaulo * and always does does a mac_ctx_changed(). 3214286441Srpaulo * 3215286441Srpaulo * The openbsd port doesn't attempt to do that - it reset things 3216286441Srpaulo * at odd states and does the add here. 3217286441Srpaulo * 3218286441Srpaulo * So, until the state handling is fixed (ie, we never reset 3219286441Srpaulo * the NIC except for a firmware failure, which should drag 3220286441Srpaulo * the NIC back to IDLE, re-setup and re-add all the mac/phy 3221286441Srpaulo * contexts that are required), let's do a dirty hack here. 3222286441Srpaulo */ 3223286441Srpaulo if (iv->is_uploaded) { 3224286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 3225286441Srpaulo device_printf(sc->sc_dev, 3226286441Srpaulo "%s: failed to add MAC\n", __func__); 3227286441Srpaulo goto out; 3228286441Srpaulo } 3229286441Srpaulo } else { 3230286441Srpaulo if ((error = iwm_mvm_mac_ctxt_add(sc, vap)) != 0) { 3231286441Srpaulo device_printf(sc->sc_dev, 3232286441Srpaulo "%s: failed to add MAC\n", __func__); 3233286441Srpaulo goto out; 3234286441Srpaulo } 3235286441Srpaulo } 3236286441Srpaulo 3237286441Srpaulo if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], 3238286441Srpaulo in->in_ni.ni_chan, 1, 1)) != 0) { 3239286441Srpaulo device_printf(sc->sc_dev, 3240286441Srpaulo "%s: failed add phy ctxt\n", __func__); 3241286441Srpaulo goto out; 3242286441Srpaulo } 3243286441Srpaulo in->in_phyctxt = &sc->sc_phyctxt[0]; 3244286441Srpaulo 3245286441Srpaulo if ((error = iwm_mvm_binding_add_vif(sc, in)) != 0) { 3246286441Srpaulo device_printf(sc->sc_dev, 3247286441Srpaulo "%s: binding cmd\n", __func__); 3248286441Srpaulo goto out; 3249286441Srpaulo } 3250286441Srpaulo 3251286441Srpaulo if ((error = iwm_mvm_add_sta(sc, in)) != 0) { 3252286441Srpaulo device_printf(sc->sc_dev, 3253286441Srpaulo "%s: failed to add MAC\n", __func__); 3254286441Srpaulo goto out; 3255286441Srpaulo } 3256286441Srpaulo 3257286441Srpaulo /* a bit superfluous? */ 3258286441Srpaulo while (sc->sc_auth_prot) 3259286441Srpaulo msleep(&sc->sc_auth_prot, &sc->sc_mtx, 0, "iwmauth", 0); 3260286441Srpaulo sc->sc_auth_prot = 1; 3261286441Srpaulo 3262286441Srpaulo duration = min(IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS, 3263286441Srpaulo 200 + in->in_ni.ni_intval); 3264286441Srpaulo min_duration = min(IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS, 3265286441Srpaulo 100 + in->in_ni.ni_intval); 3266286441Srpaulo iwm_mvm_protect_session(sc, in, duration, min_duration, 500); 3267286441Srpaulo 3268286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, 3269286441Srpaulo "%s: waiting for auth_prot\n", __func__); 3270286441Srpaulo while (sc->sc_auth_prot != 2) { 3271286441Srpaulo /* 3272286441Srpaulo * well, meh, but if the kernel is sleeping for half a 3273286441Srpaulo * second, we have bigger problems 3274286441Srpaulo */ 3275286441Srpaulo if (sc->sc_auth_prot == 0) { 3276286441Srpaulo device_printf(sc->sc_dev, 3277286441Srpaulo "%s: missed auth window!\n", __func__); 3278286441Srpaulo error = ETIMEDOUT; 3279286441Srpaulo goto out; 3280286441Srpaulo } else if (sc->sc_auth_prot == -1) { 3281286441Srpaulo device_printf(sc->sc_dev, 3282286441Srpaulo "%s: no time event, denied!\n", __func__); 3283286441Srpaulo sc->sc_auth_prot = 0; 3284286441Srpaulo error = EAUTH; 3285286441Srpaulo goto out; 3286286441Srpaulo } 3287286441Srpaulo msleep(&sc->sc_auth_prot, &sc->sc_mtx, 0, "iwmau2", 0); 3288286441Srpaulo } 3289286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET, "<-%s\n", __func__); 3290286441Srpaulo error = 0; 3291286441Srpauloout: 3292286441Srpaulo ieee80211_free_node(ni); 3293286441Srpaulo return (error); 3294286441Srpaulo} 3295286441Srpaulo 3296286441Srpaulostatic int 3297286441Srpauloiwm_assoc(struct ieee80211vap *vap, struct iwm_softc *sc) 3298286441Srpaulo{ 3299286441Srpaulo struct iwm_node *in = (struct iwm_node *)vap->iv_bss; 3300286441Srpaulo int error; 3301286441Srpaulo 3302286441Srpaulo if ((error = iwm_mvm_update_sta(sc, in)) != 0) { 3303286441Srpaulo device_printf(sc->sc_dev, 3304286441Srpaulo "%s: failed to update STA\n", __func__); 3305286441Srpaulo return error; 3306286441Srpaulo } 3307286441Srpaulo 3308286441Srpaulo in->in_assoc = 1; 3309286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { 3310286441Srpaulo device_printf(sc->sc_dev, 3311286441Srpaulo "%s: failed to update MAC\n", __func__); 3312286441Srpaulo return error; 3313286441Srpaulo } 3314286441Srpaulo 3315286441Srpaulo return 0; 3316286441Srpaulo} 3317286441Srpaulo 3318286441Srpaulostatic int 3319286441Srpauloiwm_release(struct iwm_softc *sc, struct iwm_node *in) 3320286441Srpaulo{ 3321286441Srpaulo /* 3322286441Srpaulo * Ok, so *technically* the proper set of calls for going 3323286441Srpaulo * from RUN back to SCAN is: 3324286441Srpaulo * 3325286441Srpaulo * iwm_mvm_power_mac_disable(sc, in); 3326286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 3327286441Srpaulo * iwm_mvm_rm_sta(sc, in); 3328286441Srpaulo * iwm_mvm_update_quotas(sc, NULL); 3329286441Srpaulo * iwm_mvm_mac_ctxt_changed(sc, in); 3330286441Srpaulo * iwm_mvm_binding_remove_vif(sc, in); 3331286441Srpaulo * iwm_mvm_mac_ctxt_remove(sc, in); 3332286441Srpaulo * 3333286441Srpaulo * However, that freezes the device not matter which permutations 3334286441Srpaulo * and modifications are attempted. Obviously, this driver is missing 3335286441Srpaulo * something since it works in the Linux driver, but figuring out what 3336286441Srpaulo * is missing is a little more complicated. Now, since we're going 3337286441Srpaulo * back to nothing anyway, we'll just do a complete device reset. 3338286441Srpaulo * Up your's, device! 3339286441Srpaulo */ 3340286441Srpaulo //iwm_mvm_flush_tx_path(sc, 0xf, 1); 3341286441Srpaulo iwm_stop_device(sc); 3342286441Srpaulo iwm_init_hw(sc); 3343286441Srpaulo if (in) 3344286441Srpaulo in->in_assoc = 0; 3345286441Srpaulo return 0; 3346286441Srpaulo 3347286441Srpaulo#if 0 3348286441Srpaulo int error; 3349286441Srpaulo 3350286441Srpaulo iwm_mvm_power_mac_disable(sc, in); 3351286441Srpaulo 3352286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 3353286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); 3354286441Srpaulo return error; 3355286441Srpaulo } 3356286441Srpaulo 3357286441Srpaulo if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { 3358286441Srpaulo device_printf(sc->sc_dev, "sta remove fail %d\n", error); 3359286441Srpaulo return error; 3360286441Srpaulo } 3361286441Srpaulo error = iwm_mvm_rm_sta(sc, in); 3362286441Srpaulo in->in_assoc = 0; 3363286441Srpaulo iwm_mvm_update_quotas(sc, NULL); 3364286441Srpaulo if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { 3365286441Srpaulo device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); 3366286441Srpaulo return error; 3367286441Srpaulo } 3368286441Srpaulo iwm_mvm_binding_remove_vif(sc, in); 3369286441Srpaulo 3370286441Srpaulo iwm_mvm_mac_ctxt_remove(sc, in); 3371286441Srpaulo 3372286441Srpaulo return error; 3373286441Srpaulo#endif 3374286441Srpaulo} 3375286441Srpaulo 3376286441Srpaulostatic struct ieee80211_node * 3377286441Srpauloiwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) 3378286441Srpaulo{ 3379286441Srpaulo return malloc(sizeof (struct iwm_node), M_80211_NODE, 3380286441Srpaulo M_NOWAIT | M_ZERO); 3381286441Srpaulo} 3382286441Srpaulo 3383286441Srpaulostatic void 3384286441Srpauloiwm_setrates(struct iwm_softc *sc, struct iwm_node *in) 3385286441Srpaulo{ 3386286441Srpaulo struct ieee80211_node *ni = &in->in_ni; 3387286441Srpaulo struct iwm_lq_cmd *lq = &in->in_lq; 3388286441Srpaulo int nrates = ni->ni_rates.rs_nrates; 3389286441Srpaulo int i, ridx, tab = 0; 3390286441Srpaulo int txant = 0; 3391286441Srpaulo 3392286441Srpaulo if (nrates > nitems(lq->rs_table)) { 3393286441Srpaulo device_printf(sc->sc_dev, 3394286441Srpaulo "%s: node supports %d rates, driver handles " 3395286441Srpaulo "only %zu\n", __func__, nrates, nitems(lq->rs_table)); 3396286441Srpaulo return; 3397286441Srpaulo } 3398286441Srpaulo 3399286441Srpaulo /* 3400286441Srpaulo * XXX .. and most of iwm_node is not initialised explicitly; 3401286441Srpaulo * it's all just 0x0 passed to the firmware. 3402286441Srpaulo */ 3403286441Srpaulo 3404286441Srpaulo /* first figure out which rates we should support */ 3405286441Srpaulo /* XXX TODO: this isn't 11n aware /at all/ */ 3406286441Srpaulo memset(&in->in_ridx, -1, sizeof(in->in_ridx)); 3407286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3408286441Srpaulo "%s: nrates=%d\n", __func__, nrates); 3409286441Srpaulo for (i = 0; i < nrates; i++) { 3410286441Srpaulo int rate = ni->ni_rates.rs_rates[i] & IEEE80211_RATE_VAL; 3411286441Srpaulo 3412286441Srpaulo /* Map 802.11 rate to HW rate index. */ 3413286441Srpaulo for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) 3414286441Srpaulo if (iwm_rates[ridx].rate == rate) 3415286441Srpaulo break; 3416286441Srpaulo if (ridx > IWM_RIDX_MAX) { 3417286441Srpaulo device_printf(sc->sc_dev, 3418286441Srpaulo "%s: WARNING: device rate for %d not found!\n", 3419286441Srpaulo __func__, rate); 3420286441Srpaulo } else { 3421286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3422286441Srpaulo "%s: rate: i: %d, rate=%d, ridx=%d\n", 3423286441Srpaulo __func__, 3424286441Srpaulo i, 3425286441Srpaulo rate, 3426286441Srpaulo ridx); 3427286441Srpaulo in->in_ridx[i] = ridx; 3428286441Srpaulo } 3429286441Srpaulo } 3430286441Srpaulo 3431286441Srpaulo /* then construct a lq_cmd based on those */ 3432286441Srpaulo memset(lq, 0, sizeof(*lq)); 3433286441Srpaulo lq->sta_id = IWM_STATION_ID; 3434286441Srpaulo 3435286441Srpaulo /* 3436286441Srpaulo * are these used? (we don't do SISO or MIMO) 3437286441Srpaulo * need to set them to non-zero, though, or we get an error. 3438286441Srpaulo */ 3439286441Srpaulo lq->single_stream_ant_msk = 1; 3440286441Srpaulo lq->dual_stream_ant_msk = 1; 3441286441Srpaulo 3442286441Srpaulo /* 3443286441Srpaulo * Build the actual rate selection table. 3444286441Srpaulo * The lowest bits are the rates. Additionally, 3445286441Srpaulo * CCK needs bit 9 to be set. The rest of the bits 3446286441Srpaulo * we add to the table select the tx antenna 3447286441Srpaulo * Note that we add the rates in the highest rate first 3448286441Srpaulo * (opposite of ni_rates). 3449286441Srpaulo */ 3450286441Srpaulo /* 3451286441Srpaulo * XXX TODO: this should be looping over the min of nrates 3452286441Srpaulo * and LQ_MAX_RETRY_NUM. Sigh. 3453286441Srpaulo */ 3454286441Srpaulo for (i = 0; i < nrates; i++) { 3455286441Srpaulo int nextant; 3456286441Srpaulo 3457286441Srpaulo if (txant == 0) 3458286441Srpaulo txant = IWM_FW_VALID_TX_ANT(sc); 3459286441Srpaulo nextant = 1<<(ffs(txant)-1); 3460286441Srpaulo txant &= ~nextant; 3461286441Srpaulo 3462286441Srpaulo /* 3463286441Srpaulo * Map the rate id into a rate index into 3464286441Srpaulo * our hardware table containing the 3465286441Srpaulo * configuration to use for this rate. 3466286441Srpaulo */ 3467286441Srpaulo ridx = in->in_ridx[(nrates-1)-i]; 3468286441Srpaulo tab = iwm_rates[ridx].plcp; 3469286441Srpaulo tab |= nextant << IWM_RATE_MCS_ANT_POS; 3470286441Srpaulo if (IWM_RIDX_IS_CCK(ridx)) 3471286441Srpaulo tab |= IWM_RATE_MCS_CCK_MSK; 3472286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, 3473286441Srpaulo "station rate i=%d, rate=%d, hw=%x\n", 3474286441Srpaulo i, iwm_rates[ridx].rate, tab); 3475286441Srpaulo lq->rs_table[i] = htole32(tab); 3476286441Srpaulo } 3477286441Srpaulo /* then fill the rest with the lowest possible rate */ 3478286441Srpaulo for (i = nrates; i < nitems(lq->rs_table); i++) { 3479286441Srpaulo KASSERT(tab != 0, ("invalid tab")); 3480286441Srpaulo lq->rs_table[i] = htole32(tab); 3481286441Srpaulo } 3482286441Srpaulo} 3483286441Srpaulo 3484286441Srpaulostatic int 3485286441Srpauloiwm_media_change(struct ifnet *ifp) 3486286441Srpaulo{ 3487287197Sglebius struct ieee80211vap *vap = ifp->if_softc; 3488287197Sglebius struct ieee80211com *ic = vap->iv_ic; 3489287197Sglebius struct iwm_softc *sc = ic->ic_softc; 3490286441Srpaulo int error; 3491286441Srpaulo 3492286441Srpaulo error = ieee80211_media_change(ifp); 3493286441Srpaulo if (error != ENETRESET) 3494286441Srpaulo return error; 3495286441Srpaulo 3496287197Sglebius IWM_LOCK(sc); 3497287197Sglebius if (ic->ic_nrunning > 0) { 3498287197Sglebius iwm_stop(sc); 3499286441Srpaulo iwm_init(sc); 3500286441Srpaulo } 3501287197Sglebius IWM_UNLOCK(sc); 3502286441Srpaulo return error; 3503286441Srpaulo} 3504286441Srpaulo 3505286441Srpaulo 3506286441Srpaulostatic int 3507286441Srpauloiwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) 3508286441Srpaulo{ 3509286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 3510286441Srpaulo struct ieee80211com *ic = vap->iv_ic; 3511286865Sadrian struct iwm_softc *sc = ic->ic_softc; 3512286441Srpaulo struct iwm_node *in; 3513286441Srpaulo int error; 3514286441Srpaulo 3515286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 3516286441Srpaulo "switching state %s -> %s\n", 3517286441Srpaulo ieee80211_state_name[vap->iv_state], 3518286441Srpaulo ieee80211_state_name[nstate]); 3519286441Srpaulo IEEE80211_UNLOCK(ic); 3520286441Srpaulo IWM_LOCK(sc); 3521286441Srpaulo /* disable beacon filtering if we're hopping out of RUN */ 3522286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { 3523286441Srpaulo iwm_mvm_disable_beacon_filter(sc); 3524286441Srpaulo 3525286441Srpaulo if (((in = (void *)vap->iv_bss) != NULL)) 3526286441Srpaulo in->in_assoc = 0; 3527286441Srpaulo 3528286441Srpaulo iwm_release(sc, NULL); 3529286441Srpaulo 3530286441Srpaulo /* 3531286441Srpaulo * It's impossible to directly go RUN->SCAN. If we iwm_release() 3532286441Srpaulo * above then the card will be completely reinitialized, 3533286441Srpaulo * so the driver must do everything necessary to bring the card 3534286441Srpaulo * from INIT to SCAN. 3535286441Srpaulo * 3536286441Srpaulo * Additionally, upon receiving deauth frame from AP, 3537286441Srpaulo * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH 3538286441Srpaulo * state. This will also fail with this driver, so bring the FSM 3539286441Srpaulo * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. 3540286441Srpaulo * 3541286441Srpaulo * XXX TODO: fix this for FreeBSD! 3542286441Srpaulo */ 3543286441Srpaulo if (nstate == IEEE80211_S_SCAN || 3544286441Srpaulo nstate == IEEE80211_S_AUTH || 3545286441Srpaulo nstate == IEEE80211_S_ASSOC) { 3546286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 3547286441Srpaulo "Force transition to INIT; MGT=%d\n", arg); 3548286441Srpaulo IWM_UNLOCK(sc); 3549286441Srpaulo IEEE80211_LOCK(ic); 3550286441Srpaulo vap->iv_newstate(vap, IEEE80211_S_INIT, arg); 3551286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_STATE, 3552286441Srpaulo "Going INIT->SCAN\n"); 3553286441Srpaulo nstate = IEEE80211_S_SCAN; 3554286441Srpaulo IEEE80211_UNLOCK(ic); 3555286441Srpaulo IWM_LOCK(sc); 3556286441Srpaulo } 3557286441Srpaulo } 3558286441Srpaulo 3559286441Srpaulo switch (nstate) { 3560286441Srpaulo case IEEE80211_S_INIT: 3561286441Srpaulo sc->sc_scanband = 0; 3562286441Srpaulo break; 3563286441Srpaulo 3564286441Srpaulo case IEEE80211_S_AUTH: 3565286441Srpaulo if ((error = iwm_auth(vap, sc)) != 0) { 3566286441Srpaulo device_printf(sc->sc_dev, 3567286441Srpaulo "%s: could not move to auth state: %d\n", 3568286441Srpaulo __func__, error); 3569286441Srpaulo break; 3570286441Srpaulo } 3571286441Srpaulo break; 3572286441Srpaulo 3573286441Srpaulo case IEEE80211_S_ASSOC: 3574286441Srpaulo if ((error = iwm_assoc(vap, sc)) != 0) { 3575286441Srpaulo device_printf(sc->sc_dev, 3576286441Srpaulo "%s: failed to associate: %d\n", __func__, 3577286441Srpaulo error); 3578286441Srpaulo break; 3579286441Srpaulo } 3580286441Srpaulo break; 3581286441Srpaulo 3582286441Srpaulo case IEEE80211_S_RUN: 3583286441Srpaulo { 3584286441Srpaulo struct iwm_host_cmd cmd = { 3585286441Srpaulo .id = IWM_LQ_CMD, 3586286441Srpaulo .len = { sizeof(in->in_lq), }, 3587286441Srpaulo .flags = IWM_CMD_SYNC, 3588286441Srpaulo }; 3589286441Srpaulo 3590286441Srpaulo /* Update the association state, now we have it all */ 3591286441Srpaulo /* (eg associd comes in at this point */ 3592286441Srpaulo error = iwm_assoc(vap, sc); 3593286441Srpaulo if (error != 0) { 3594286441Srpaulo device_printf(sc->sc_dev, 3595286441Srpaulo "%s: failed to update association state: %d\n", 3596286441Srpaulo __func__, 3597286441Srpaulo error); 3598286441Srpaulo break; 3599286441Srpaulo } 3600286441Srpaulo 3601286441Srpaulo in = (struct iwm_node *)vap->iv_bss; 3602286441Srpaulo iwm_mvm_power_mac_update_mode(sc, in); 3603286441Srpaulo iwm_mvm_enable_beacon_filter(sc, in); 3604286441Srpaulo iwm_mvm_update_quotas(sc, in); 3605286441Srpaulo iwm_setrates(sc, in); 3606286441Srpaulo 3607286441Srpaulo cmd.data[0] = &in->in_lq; 3608286441Srpaulo if ((error = iwm_send_cmd(sc, &cmd)) != 0) { 3609286441Srpaulo device_printf(sc->sc_dev, 3610286441Srpaulo "%s: IWM_LQ_CMD failed\n", __func__); 3611286441Srpaulo } 3612286441Srpaulo 3613286441Srpaulo break; 3614286441Srpaulo } 3615286441Srpaulo 3616286441Srpaulo default: 3617286441Srpaulo break; 3618286441Srpaulo } 3619286441Srpaulo IWM_UNLOCK(sc); 3620286441Srpaulo IEEE80211_LOCK(ic); 3621286441Srpaulo 3622286441Srpaulo return (ivp->iv_newstate(vap, nstate, arg)); 3623286441Srpaulo} 3624286441Srpaulo 3625286441Srpaulovoid 3626286441Srpauloiwm_endscan_cb(void *arg, int pending) 3627286441Srpaulo{ 3628286441Srpaulo struct iwm_softc *sc = arg; 3629287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3630286441Srpaulo int done; 3631286441Srpaulo int error; 3632286441Srpaulo 3633286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_SCAN | IWM_DEBUG_TRACE, 3634286441Srpaulo "%s: scan ended\n", 3635286441Srpaulo __func__); 3636286441Srpaulo 3637286441Srpaulo IWM_LOCK(sc); 3638286441Srpaulo if (sc->sc_scanband == IEEE80211_CHAN_2GHZ && 3639286441Srpaulo sc->sc_nvm.sku_cap_band_52GHz_enable) { 3640286441Srpaulo done = 0; 3641286441Srpaulo if ((error = iwm_mvm_scan_request(sc, 3642286441Srpaulo IEEE80211_CHAN_5GHZ, 0, NULL, 0)) != 0) { 3643286441Srpaulo device_printf(sc->sc_dev, "could not initiate scan\n"); 3644286441Srpaulo done = 1; 3645286441Srpaulo } 3646286441Srpaulo } else { 3647286441Srpaulo done = 1; 3648286441Srpaulo } 3649286441Srpaulo 3650286441Srpaulo if (done) { 3651286441Srpaulo IWM_UNLOCK(sc); 3652286441Srpaulo ieee80211_scan_done(TAILQ_FIRST(&ic->ic_vaps)); 3653286441Srpaulo IWM_LOCK(sc); 3654286441Srpaulo sc->sc_scanband = 0; 3655286441Srpaulo } 3656286441Srpaulo IWM_UNLOCK(sc); 3657286441Srpaulo} 3658286441Srpaulo 3659286441Srpaulostatic int 3660286441Srpauloiwm_init_hw(struct iwm_softc *sc) 3661286441Srpaulo{ 3662287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 3663286441Srpaulo int error, i, qid; 3664286441Srpaulo 3665286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) 3666286441Srpaulo return error; 3667286441Srpaulo 3668286441Srpaulo if ((error = iwm_run_init_mvm_ucode(sc, 0)) != 0) { 3669286441Srpaulo return error; 3670286441Srpaulo } 3671286441Srpaulo 3672286441Srpaulo /* 3673286441Srpaulo * should stop and start HW since that INIT 3674286441Srpaulo * image just loaded 3675286441Srpaulo */ 3676286441Srpaulo iwm_stop_device(sc); 3677286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 3678286441Srpaulo device_printf(sc->sc_dev, "could not initialize hardware\n"); 3679286441Srpaulo return error; 3680286441Srpaulo } 3681286441Srpaulo 3682286441Srpaulo /* omstart, this time with the regular firmware */ 3683286441Srpaulo error = iwm_mvm_load_ucode_wait_alive(sc, IWM_UCODE_TYPE_REGULAR); 3684286441Srpaulo if (error) { 3685286441Srpaulo device_printf(sc->sc_dev, "could not load firmware\n"); 3686286441Srpaulo goto error; 3687286441Srpaulo } 3688286441Srpaulo 3689286441Srpaulo if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) 3690286441Srpaulo goto error; 3691286441Srpaulo 3692286441Srpaulo /* Send phy db control command and then phy db calibration*/ 3693286441Srpaulo if ((error = iwm_send_phy_db_data(sc)) != 0) 3694286441Srpaulo goto error; 3695286441Srpaulo 3696286441Srpaulo if ((error = iwm_send_phy_cfg_cmd(sc)) != 0) 3697286441Srpaulo goto error; 3698286441Srpaulo 3699286441Srpaulo /* Add auxiliary station for scanning */ 3700286441Srpaulo if ((error = iwm_mvm_add_aux_sta(sc)) != 0) 3701286441Srpaulo goto error; 3702286441Srpaulo 3703286441Srpaulo for (i = 0; i < IWM_NUM_PHY_CTX; i++) { 3704286441Srpaulo /* 3705286441Srpaulo * The channel used here isn't relevant as it's 3706286441Srpaulo * going to be overwritten in the other flows. 3707286441Srpaulo * For now use the first channel we have. 3708286441Srpaulo */ 3709286441Srpaulo if ((error = iwm_mvm_phy_ctxt_add(sc, 3710286441Srpaulo &sc->sc_phyctxt[i], &ic->ic_channels[1], 1, 1)) != 0) 3711286441Srpaulo goto error; 3712286441Srpaulo } 3713286441Srpaulo 3714286441Srpaulo error = iwm_mvm_power_update_device(sc); 3715286441Srpaulo if (error) 3716286441Srpaulo goto error; 3717286441Srpaulo 3718286441Srpaulo /* Mark TX rings as active. */ 3719286441Srpaulo for (qid = 0; qid < 4; qid++) { 3720286441Srpaulo iwm_enable_txq(sc, qid, qid); 3721286441Srpaulo } 3722286441Srpaulo 3723286441Srpaulo return 0; 3724286441Srpaulo 3725286441Srpaulo error: 3726286441Srpaulo iwm_stop_device(sc); 3727286441Srpaulo return error; 3728286441Srpaulo} 3729286441Srpaulo 3730286441Srpaulo/* Allow multicast from our BSSID. */ 3731286441Srpaulostatic int 3732286441Srpauloiwm_allow_mcast(struct ieee80211vap *vap, struct iwm_softc *sc) 3733286441Srpaulo{ 3734286441Srpaulo struct ieee80211_node *ni = vap->iv_bss; 3735286441Srpaulo struct iwm_mcast_filter_cmd *cmd; 3736286441Srpaulo size_t size; 3737286441Srpaulo int error; 3738286441Srpaulo 3739286441Srpaulo size = roundup(sizeof(*cmd), 4); 3740286441Srpaulo cmd = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); 3741286441Srpaulo if (cmd == NULL) 3742286441Srpaulo return ENOMEM; 3743286441Srpaulo cmd->filter_own = 1; 3744286441Srpaulo cmd->port_id = 0; 3745286441Srpaulo cmd->count = 0; 3746286441Srpaulo cmd->pass_all = 1; 3747286441Srpaulo IEEE80211_ADDR_COPY(cmd->bssid, ni->ni_bssid); 3748286441Srpaulo 3749286441Srpaulo error = iwm_mvm_send_cmd_pdu(sc, IWM_MCAST_FILTER_CMD, 3750286441Srpaulo IWM_CMD_SYNC, size, cmd); 3751286441Srpaulo free(cmd, M_DEVBUF); 3752286441Srpaulo 3753286441Srpaulo return (error); 3754286441Srpaulo} 3755286441Srpaulo 3756286441Srpaulostatic void 3757287197Sglebiusiwm_init(struct iwm_softc *sc) 3758286441Srpaulo{ 3759286441Srpaulo int error; 3760286441Srpaulo 3761286441Srpaulo if (sc->sc_flags & IWM_FLAG_HW_INITED) { 3762286441Srpaulo return; 3763286441Srpaulo } 3764286441Srpaulo sc->sc_generation++; 3765286441Srpaulo sc->sc_flags &= ~IWM_FLAG_STOPPED; 3766286441Srpaulo 3767286441Srpaulo if ((error = iwm_init_hw(sc)) != 0) { 3768287197Sglebius iwm_stop(sc); 3769286441Srpaulo return; 3770286441Srpaulo } 3771286441Srpaulo 3772286441Srpaulo /* 3773286441Srpaulo * Ok, firmware loaded and we are jogging 3774286441Srpaulo */ 3775286441Srpaulo sc->sc_flags |= IWM_FLAG_HW_INITED; 3776286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 3777286441Srpaulo} 3778286441Srpaulo 3779287197Sglebiusstatic int 3780287197Sglebiusiwm_transmit(struct ieee80211com *ic, struct mbuf *m) 3781286441Srpaulo{ 3782287197Sglebius struct iwm_softc *sc; 3783287197Sglebius int error; 3784286441Srpaulo 3785287197Sglebius sc = ic->ic_softc; 3786287197Sglebius 3787286441Srpaulo IWM_LOCK(sc); 3788287197Sglebius if ((sc->sc_flags & IWM_FLAG_HW_INITED) == 0) { 3789287197Sglebius IWM_UNLOCK(sc); 3790287197Sglebius return (ENXIO); 3791287197Sglebius } 3792287197Sglebius error = mbufq_enqueue(&sc->sc_snd, m); 3793287197Sglebius if (error) { 3794287197Sglebius IWM_UNLOCK(sc); 3795287197Sglebius return (error); 3796287197Sglebius } 3797287197Sglebius iwm_start(sc); 3798286441Srpaulo IWM_UNLOCK(sc); 3799287197Sglebius return (0); 3800286441Srpaulo} 3801286441Srpaulo 3802287197Sglebius/* 3803287197Sglebius * Dequeue packets from sendq and call send. 3804287197Sglebius */ 3805286441Srpaulostatic void 3806287197Sglebiusiwm_start(struct iwm_softc *sc) 3807286441Srpaulo{ 3808286441Srpaulo struct ieee80211_node *ni; 3809286441Srpaulo struct mbuf *m; 3810286441Srpaulo int ac = 0; 3811286441Srpaulo 3812286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "->%s\n", __func__); 3813287197Sglebius while (sc->qfullmsk == 0 && 3814287197Sglebius (m = mbufq_dequeue(&sc->sc_snd)) != NULL) { 3815286441Srpaulo ni = (struct ieee80211_node *)m->m_pkthdr.rcvif; 3816286441Srpaulo if (iwm_tx(sc, m, ni, ac) != 0) { 3817287197Sglebius if_inc_counter(ni->ni_vap->iv_ifp, 3818287197Sglebius IFCOUNTER_OERRORS, 1); 3819286441Srpaulo ieee80211_free_node(ni); 3820286441Srpaulo continue; 3821286441Srpaulo } 3822287197Sglebius sc->sc_tx_timer = 15; 3823286441Srpaulo } 3824286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); 3825286441Srpaulo} 3826286441Srpaulo 3827286441Srpaulostatic void 3828287197Sglebiusiwm_stop(struct iwm_softc *sc) 3829286441Srpaulo{ 3830286441Srpaulo 3831286441Srpaulo sc->sc_flags &= ~IWM_FLAG_HW_INITED; 3832286441Srpaulo sc->sc_flags |= IWM_FLAG_STOPPED; 3833286441Srpaulo sc->sc_generation++; 3834286441Srpaulo sc->sc_scanband = 0; 3835286441Srpaulo sc->sc_auth_prot = 0; 3836286441Srpaulo sc->sc_tx_timer = 0; 3837286441Srpaulo iwm_stop_device(sc); 3838286441Srpaulo} 3839286441Srpaulo 3840286441Srpaulostatic void 3841286441Srpauloiwm_watchdog(void *arg) 3842286441Srpaulo{ 3843286441Srpaulo struct iwm_softc *sc = arg; 3844286441Srpaulo 3845286441Srpaulo if (sc->sc_tx_timer > 0) { 3846286441Srpaulo if (--sc->sc_tx_timer == 0) { 3847286441Srpaulo device_printf(sc->sc_dev, "device timeout\n"); 3848286441Srpaulo#ifdef IWM_DEBUG 3849286441Srpaulo iwm_nic_error(sc); 3850286441Srpaulo#endif 3851287197Sglebius iwm_stop(sc); 3852287197Sglebius counter_u64_add(sc->sc_ic.ic_oerrors, 1); 3853286441Srpaulo return; 3854286441Srpaulo } 3855286441Srpaulo } 3856286441Srpaulo callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); 3857286441Srpaulo} 3858286441Srpaulo 3859287197Sglebiusstatic void 3860287197Sglebiusiwm_parent(struct ieee80211com *ic) 3861286441Srpaulo{ 3862287197Sglebius struct iwm_softc *sc = ic->ic_softc; 3863287197Sglebius int startall = 0; 3864286441Srpaulo 3865287197Sglebius IWM_LOCK(sc); 3866287197Sglebius if (ic->ic_nrunning > 0) { 3867287197Sglebius if (!(sc->sc_flags & IWM_FLAG_HW_INITED)) { 3868287197Sglebius iwm_init(sc); 3869287197Sglebius startall = 1; 3870286441Srpaulo } 3871287197Sglebius } else if (sc->sc_flags & IWM_FLAG_HW_INITED) 3872287197Sglebius iwm_stop(sc); 3873287197Sglebius IWM_UNLOCK(sc); 3874287197Sglebius if (startall) 3875287197Sglebius ieee80211_start_all(ic); 3876286441Srpaulo} 3877286441Srpaulo 3878286441Srpaulo/* 3879286441Srpaulo * The interrupt side of things 3880286441Srpaulo */ 3881286441Srpaulo 3882286441Srpaulo/* 3883286441Srpaulo * error dumping routines are from iwlwifi/mvm/utils.c 3884286441Srpaulo */ 3885286441Srpaulo 3886286441Srpaulo/* 3887286441Srpaulo * Note: This structure is read from the device with IO accesses, 3888286441Srpaulo * and the reading already does the endian conversion. As it is 3889286441Srpaulo * read with uint32_t-sized accesses, any members with a different size 3890286441Srpaulo * need to be ordered correctly though! 3891286441Srpaulo */ 3892286441Srpaulostruct iwm_error_event_table { 3893286441Srpaulo uint32_t valid; /* (nonzero) valid, (0) log is empty */ 3894286441Srpaulo uint32_t error_id; /* type of error */ 3895286441Srpaulo uint32_t pc; /* program counter */ 3896286441Srpaulo uint32_t blink1; /* branch link */ 3897286441Srpaulo uint32_t blink2; /* branch link */ 3898286441Srpaulo uint32_t ilink1; /* interrupt link */ 3899286441Srpaulo uint32_t ilink2; /* interrupt link */ 3900286441Srpaulo uint32_t data1; /* error-specific data */ 3901286441Srpaulo uint32_t data2; /* error-specific data */ 3902286441Srpaulo uint32_t data3; /* error-specific data */ 3903286441Srpaulo uint32_t bcon_time; /* beacon timer */ 3904286441Srpaulo uint32_t tsf_low; /* network timestamp function timer */ 3905286441Srpaulo uint32_t tsf_hi; /* network timestamp function timer */ 3906286441Srpaulo uint32_t gp1; /* GP1 timer register */ 3907286441Srpaulo uint32_t gp2; /* GP2 timer register */ 3908286441Srpaulo uint32_t gp3; /* GP3 timer register */ 3909286441Srpaulo uint32_t ucode_ver; /* uCode version */ 3910286441Srpaulo uint32_t hw_ver; /* HW Silicon version */ 3911286441Srpaulo uint32_t brd_ver; /* HW board version */ 3912286441Srpaulo uint32_t log_pc; /* log program counter */ 3913286441Srpaulo uint32_t frame_ptr; /* frame pointer */ 3914286441Srpaulo uint32_t stack_ptr; /* stack pointer */ 3915286441Srpaulo uint32_t hcmd; /* last host command header */ 3916286441Srpaulo uint32_t isr0; /* isr status register LMPM_NIC_ISR0: 3917286441Srpaulo * rxtx_flag */ 3918286441Srpaulo uint32_t isr1; /* isr status register LMPM_NIC_ISR1: 3919286441Srpaulo * host_flag */ 3920286441Srpaulo uint32_t isr2; /* isr status register LMPM_NIC_ISR2: 3921286441Srpaulo * enc_flag */ 3922286441Srpaulo uint32_t isr3; /* isr status register LMPM_NIC_ISR3: 3923286441Srpaulo * time_flag */ 3924286441Srpaulo uint32_t isr4; /* isr status register LMPM_NIC_ISR4: 3925286441Srpaulo * wico interrupt */ 3926286441Srpaulo uint32_t isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ 3927286441Srpaulo uint32_t wait_event; /* wait event() caller address */ 3928286441Srpaulo uint32_t l2p_control; /* L2pControlField */ 3929286441Srpaulo uint32_t l2p_duration; /* L2pDurationField */ 3930286441Srpaulo uint32_t l2p_mhvalid; /* L2pMhValidBits */ 3931286441Srpaulo uint32_t l2p_addr_match; /* L2pAddrMatchStat */ 3932286441Srpaulo uint32_t lmpm_pmg_sel; /* indicate which clocks are turned on 3933286441Srpaulo * (LMPM_PMG_SEL) */ 3934286441Srpaulo uint32_t u_timestamp; /* indicate when the date and time of the 3935286441Srpaulo * compilation */ 3936286441Srpaulo uint32_t flow_handler; /* FH read/write pointers, RX credit */ 3937286441Srpaulo} __packed; 3938286441Srpaulo 3939286441Srpaulo#define ERROR_START_OFFSET (1 * sizeof(uint32_t)) 3940286441Srpaulo#define ERROR_ELEM_SIZE (7 * sizeof(uint32_t)) 3941286441Srpaulo 3942286441Srpaulo#ifdef IWM_DEBUG 3943286441Srpaulostruct { 3944286441Srpaulo const char *name; 3945286441Srpaulo uint8_t num; 3946286441Srpaulo} advanced_lookup[] = { 3947286441Srpaulo { "NMI_INTERRUPT_WDG", 0x34 }, 3948286441Srpaulo { "SYSASSERT", 0x35 }, 3949286441Srpaulo { "UCODE_VERSION_MISMATCH", 0x37 }, 3950286441Srpaulo { "BAD_COMMAND", 0x38 }, 3951286441Srpaulo { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, 3952286441Srpaulo { "FATAL_ERROR", 0x3D }, 3953286441Srpaulo { "NMI_TRM_HW_ERR", 0x46 }, 3954286441Srpaulo { "NMI_INTERRUPT_TRM", 0x4C }, 3955286441Srpaulo { "NMI_INTERRUPT_BREAK_POINT", 0x54 }, 3956286441Srpaulo { "NMI_INTERRUPT_WDG_RXF_FULL", 0x5C }, 3957286441Srpaulo { "NMI_INTERRUPT_WDG_NO_RBD_RXF_FULL", 0x64 }, 3958286441Srpaulo { "NMI_INTERRUPT_HOST", 0x66 }, 3959286441Srpaulo { "NMI_INTERRUPT_ACTION_PT", 0x7C }, 3960286441Srpaulo { "NMI_INTERRUPT_UNKNOWN", 0x84 }, 3961286441Srpaulo { "NMI_INTERRUPT_INST_ACTION_PT", 0x86 }, 3962286441Srpaulo { "ADVANCED_SYSASSERT", 0 }, 3963286441Srpaulo}; 3964286441Srpaulo 3965286441Srpaulostatic const char * 3966286441Srpauloiwm_desc_lookup(uint32_t num) 3967286441Srpaulo{ 3968286441Srpaulo int i; 3969286441Srpaulo 3970286441Srpaulo for (i = 0; i < nitems(advanced_lookup) - 1; i++) 3971286441Srpaulo if (advanced_lookup[i].num == num) 3972286441Srpaulo return advanced_lookup[i].name; 3973286441Srpaulo 3974286441Srpaulo /* No entry matches 'num', so it is the last: ADVANCED_SYSASSERT */ 3975286441Srpaulo return advanced_lookup[i].name; 3976286441Srpaulo} 3977286441Srpaulo 3978286441Srpaulo/* 3979286441Srpaulo * Support for dumping the error log seemed like a good idea ... 3980286441Srpaulo * but it's mostly hex junk and the only sensible thing is the 3981286441Srpaulo * hw/ucode revision (which we know anyway). Since it's here, 3982286441Srpaulo * I'll just leave it in, just in case e.g. the Intel guys want to 3983286441Srpaulo * help us decipher some "ADVANCED_SYSASSERT" later. 3984286441Srpaulo */ 3985286441Srpaulostatic void 3986286441Srpauloiwm_nic_error(struct iwm_softc *sc) 3987286441Srpaulo{ 3988286441Srpaulo struct iwm_error_event_table table; 3989286441Srpaulo uint32_t base; 3990286441Srpaulo 3991286441Srpaulo device_printf(sc->sc_dev, "dumping device error log\n"); 3992286441Srpaulo base = sc->sc_uc.uc_error_event_table; 3993286441Srpaulo if (base < 0x800000 || base >= 0x80C000) { 3994286441Srpaulo device_printf(sc->sc_dev, 3995286441Srpaulo "Not valid error log pointer 0x%08x\n", base); 3996286441Srpaulo return; 3997286441Srpaulo } 3998286441Srpaulo 3999286441Srpaulo if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t)) != 0) { 4000286441Srpaulo device_printf(sc->sc_dev, "reading errlog failed\n"); 4001286441Srpaulo return; 4002286441Srpaulo } 4003286441Srpaulo 4004286441Srpaulo if (!table.valid) { 4005286441Srpaulo device_printf(sc->sc_dev, "errlog not found, skipping\n"); 4006286441Srpaulo return; 4007286441Srpaulo } 4008286441Srpaulo 4009286441Srpaulo if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { 4010286441Srpaulo device_printf(sc->sc_dev, "Start IWL Error Log Dump:\n"); 4011286441Srpaulo device_printf(sc->sc_dev, "Status: 0x%x, count: %d\n", 4012286441Srpaulo sc->sc_flags, table.valid); 4013286441Srpaulo } 4014286441Srpaulo 4015286441Srpaulo device_printf(sc->sc_dev, "0x%08X | %-28s\n", table.error_id, 4016286441Srpaulo iwm_desc_lookup(table.error_id)); 4017286441Srpaulo device_printf(sc->sc_dev, "%08X | uPc\n", table.pc); 4018286441Srpaulo device_printf(sc->sc_dev, "%08X | branchlink1\n", table.blink1); 4019286441Srpaulo device_printf(sc->sc_dev, "%08X | branchlink2\n", table.blink2); 4020286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink1\n", table.ilink1); 4021286441Srpaulo device_printf(sc->sc_dev, "%08X | interruptlink2\n", table.ilink2); 4022286441Srpaulo device_printf(sc->sc_dev, "%08X | data1\n", table.data1); 4023286441Srpaulo device_printf(sc->sc_dev, "%08X | data2\n", table.data2); 4024286441Srpaulo device_printf(sc->sc_dev, "%08X | data3\n", table.data3); 4025286441Srpaulo device_printf(sc->sc_dev, "%08X | beacon time\n", table.bcon_time); 4026286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf low\n", table.tsf_low); 4027286441Srpaulo device_printf(sc->sc_dev, "%08X | tsf hi\n", table.tsf_hi); 4028286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp1\n", table.gp1); 4029286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp2\n", table.gp2); 4030286441Srpaulo device_printf(sc->sc_dev, "%08X | time gp3\n", table.gp3); 4031286441Srpaulo device_printf(sc->sc_dev, "%08X | uCode version\n", table.ucode_ver); 4032286441Srpaulo device_printf(sc->sc_dev, "%08X | hw version\n", table.hw_ver); 4033286441Srpaulo device_printf(sc->sc_dev, "%08X | board version\n", table.brd_ver); 4034286441Srpaulo device_printf(sc->sc_dev, "%08X | hcmd\n", table.hcmd); 4035286441Srpaulo device_printf(sc->sc_dev, "%08X | isr0\n", table.isr0); 4036286441Srpaulo device_printf(sc->sc_dev, "%08X | isr1\n", table.isr1); 4037286441Srpaulo device_printf(sc->sc_dev, "%08X | isr2\n", table.isr2); 4038286441Srpaulo device_printf(sc->sc_dev, "%08X | isr3\n", table.isr3); 4039286441Srpaulo device_printf(sc->sc_dev, "%08X | isr4\n", table.isr4); 4040286441Srpaulo device_printf(sc->sc_dev, "%08X | isr_pref\n", table.isr_pref); 4041286441Srpaulo device_printf(sc->sc_dev, "%08X | wait_event\n", table.wait_event); 4042286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_control\n", table.l2p_control); 4043286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_duration\n", table.l2p_duration); 4044286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_mhvalid\n", table.l2p_mhvalid); 4045286441Srpaulo device_printf(sc->sc_dev, "%08X | l2p_addr_match\n", table.l2p_addr_match); 4046286441Srpaulo device_printf(sc->sc_dev, "%08X | lmpm_pmg_sel\n", table.lmpm_pmg_sel); 4047286441Srpaulo device_printf(sc->sc_dev, "%08X | timestamp\n", table.u_timestamp); 4048286441Srpaulo device_printf(sc->sc_dev, "%08X | flow_handler\n", table.flow_handler); 4049286441Srpaulo} 4050286441Srpaulo#endif 4051286441Srpaulo 4052286441Srpaulo#define SYNC_RESP_STRUCT(_var_, _pkt_) \ 4053286441Srpaulodo { \ 4054286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ 4055286441Srpaulo _var_ = (void *)((_pkt_)+1); \ 4056286441Srpaulo} while (/*CONSTCOND*/0) 4057286441Srpaulo 4058286441Srpaulo#define SYNC_RESP_PTR(_ptr_, _len_, _pkt_) \ 4059286441Srpaulodo { \ 4060286441Srpaulo bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_POSTREAD);\ 4061286441Srpaulo _ptr_ = (void *)((_pkt_)+1); \ 4062286441Srpaulo} while (/*CONSTCOND*/0) 4063286441Srpaulo 4064286441Srpaulo#define ADVANCE_RXQ(sc) (sc->rxq.cur = (sc->rxq.cur + 1) % IWM_RX_RING_COUNT); 4065286441Srpaulo 4066286441Srpaulo/* 4067286441Srpaulo * Process an IWM_CSR_INT_BIT_FH_RX or IWM_CSR_INT_BIT_SW_RX interrupt. 4068286441Srpaulo * Basic structure from if_iwn 4069286441Srpaulo */ 4070286441Srpaulostatic void 4071286441Srpauloiwm_notif_intr(struct iwm_softc *sc) 4072286441Srpaulo{ 4073286441Srpaulo uint16_t hw; 4074286441Srpaulo 4075286441Srpaulo bus_dmamap_sync(sc->rxq.stat_dma.tag, sc->rxq.stat_dma.map, 4076286441Srpaulo BUS_DMASYNC_POSTREAD); 4077286441Srpaulo 4078286441Srpaulo hw = le16toh(sc->rxq.stat->closed_rb_num) & 0xfff; 4079286441Srpaulo while (sc->rxq.cur != hw) { 4080286441Srpaulo struct iwm_rx_ring *ring = &sc->rxq; 4081286441Srpaulo struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; 4082286441Srpaulo struct iwm_rx_packet *pkt; 4083286441Srpaulo struct iwm_cmd_response *cresp; 4084286441Srpaulo int qid, idx; 4085286441Srpaulo 4086286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 4087286441Srpaulo BUS_DMASYNC_POSTREAD); 4088286441Srpaulo pkt = mtod(data->m, struct iwm_rx_packet *); 4089286441Srpaulo 4090286441Srpaulo qid = pkt->hdr.qid & ~0x80; 4091286441Srpaulo idx = pkt->hdr.idx; 4092286441Srpaulo 4093286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 4094286441Srpaulo "rx packet qid=%d idx=%d flags=%x type=%x %d %d\n", 4095286441Srpaulo pkt->hdr.qid & ~0x80, pkt->hdr.idx, pkt->hdr.flags, 4096286441Srpaulo pkt->hdr.code, sc->rxq.cur, hw); 4097286441Srpaulo 4098286441Srpaulo /* 4099286441Srpaulo * randomly get these from the firmware, no idea why. 4100286441Srpaulo * they at least seem harmless, so just ignore them for now 4101286441Srpaulo */ 4102286441Srpaulo if (__predict_false((pkt->hdr.code == 0 && qid == 0 && idx == 0) 4103286441Srpaulo || pkt->len_n_flags == htole32(0x55550000))) { 4104286441Srpaulo ADVANCE_RXQ(sc); 4105286441Srpaulo continue; 4106286441Srpaulo } 4107286441Srpaulo 4108286441Srpaulo switch (pkt->hdr.code) { 4109286441Srpaulo case IWM_REPLY_RX_PHY_CMD: 4110286441Srpaulo iwm_mvm_rx_rx_phy_cmd(sc, pkt, data); 4111286441Srpaulo break; 4112286441Srpaulo 4113286441Srpaulo case IWM_REPLY_RX_MPDU_CMD: 4114286441Srpaulo iwm_mvm_rx_rx_mpdu(sc, pkt, data); 4115286441Srpaulo break; 4116286441Srpaulo 4117286441Srpaulo case IWM_TX_CMD: 4118286441Srpaulo iwm_mvm_rx_tx_cmd(sc, pkt, data); 4119286441Srpaulo break; 4120286441Srpaulo 4121286441Srpaulo case IWM_MISSED_BEACONS_NOTIFICATION: { 4122286441Srpaulo struct iwm_missed_beacons_notif *resp; 4123286441Srpaulo int missed; 4124286441Srpaulo 4125286441Srpaulo /* XXX look at mac_id to determine interface ID */ 4126287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4127286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4128286441Srpaulo 4129286441Srpaulo SYNC_RESP_STRUCT(resp, pkt); 4130286441Srpaulo missed = le32toh(resp->consec_missed_beacons); 4131286441Srpaulo 4132286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_BEACON | IWM_DEBUG_STATE, 4133286441Srpaulo "%s: MISSED_BEACON: mac_id=%d, " 4134286441Srpaulo "consec_since_last_rx=%d, consec=%d, num_expect=%d " 4135286441Srpaulo "num_rx=%d\n", 4136286441Srpaulo __func__, 4137286441Srpaulo le32toh(resp->mac_id), 4138286441Srpaulo le32toh(resp->consec_missed_beacons_since_last_rx), 4139286441Srpaulo le32toh(resp->consec_missed_beacons), 4140286441Srpaulo le32toh(resp->num_expected_beacons), 4141286441Srpaulo le32toh(resp->num_recvd_beacons)); 4142286441Srpaulo 4143286441Srpaulo /* Be paranoid */ 4144286441Srpaulo if (vap == NULL) 4145286441Srpaulo break; 4146286441Srpaulo 4147286441Srpaulo /* XXX no net80211 locking? */ 4148286441Srpaulo if (vap->iv_state == IEEE80211_S_RUN && 4149286441Srpaulo (ic->ic_flags & IEEE80211_F_SCAN) == 0) { 4150286441Srpaulo if (missed > vap->iv_bmissthreshold) { 4151286441Srpaulo /* XXX bad locking; turn into task */ 4152286441Srpaulo IWM_UNLOCK(sc); 4153286441Srpaulo ieee80211_beacon_miss(ic); 4154286441Srpaulo IWM_LOCK(sc); 4155286441Srpaulo } 4156286441Srpaulo } 4157286441Srpaulo 4158286441Srpaulo break; } 4159286441Srpaulo 4160286441Srpaulo case IWM_MVM_ALIVE: { 4161286441Srpaulo struct iwm_mvm_alive_resp *resp; 4162286441Srpaulo SYNC_RESP_STRUCT(resp, pkt); 4163286441Srpaulo 4164286441Srpaulo sc->sc_uc.uc_error_event_table 4165286441Srpaulo = le32toh(resp->error_event_table_ptr); 4166286441Srpaulo sc->sc_uc.uc_log_event_table 4167286441Srpaulo = le32toh(resp->log_event_table_ptr); 4168286441Srpaulo sc->sched_base = le32toh(resp->scd_base_ptr); 4169286441Srpaulo sc->sc_uc.uc_ok = resp->status == IWM_ALIVE_STATUS_OK; 4170286441Srpaulo 4171286441Srpaulo sc->sc_uc.uc_intr = 1; 4172286441Srpaulo wakeup(&sc->sc_uc); 4173286441Srpaulo break; } 4174286441Srpaulo 4175286441Srpaulo case IWM_CALIB_RES_NOTIF_PHY_DB: { 4176286441Srpaulo struct iwm_calib_res_notif_phy_db *phy_db_notif; 4177286441Srpaulo SYNC_RESP_STRUCT(phy_db_notif, pkt); 4178286441Srpaulo 4179286441Srpaulo iwm_phy_db_set_section(sc, phy_db_notif); 4180286441Srpaulo 4181286441Srpaulo break; } 4182286441Srpaulo 4183286441Srpaulo case IWM_STATISTICS_NOTIFICATION: { 4184286441Srpaulo struct iwm_notif_statistics *stats; 4185286441Srpaulo SYNC_RESP_STRUCT(stats, pkt); 4186286441Srpaulo memcpy(&sc->sc_stats, stats, sizeof(sc->sc_stats)); 4187286441Srpaulo sc->sc_noise = iwm_get_noise(&stats->rx.general); 4188286441Srpaulo break; } 4189286441Srpaulo 4190286441Srpaulo case IWM_NVM_ACCESS_CMD: 4191286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 4192286441Srpaulo bus_dmamap_sync(sc->rxq.data_dmat, data->map, 4193286441Srpaulo BUS_DMASYNC_POSTREAD); 4194286441Srpaulo memcpy(sc->sc_cmd_resp, 4195286441Srpaulo pkt, sizeof(sc->sc_cmd_resp)); 4196286441Srpaulo } 4197286441Srpaulo break; 4198286441Srpaulo 4199286441Srpaulo case IWM_PHY_CONFIGURATION_CMD: 4200286441Srpaulo case IWM_TX_ANT_CONFIGURATION_CMD: 4201286441Srpaulo case IWM_ADD_STA: 4202286441Srpaulo case IWM_MAC_CONTEXT_CMD: 4203286441Srpaulo case IWM_REPLY_SF_CFG_CMD: 4204286441Srpaulo case IWM_POWER_TABLE_CMD: 4205286441Srpaulo case IWM_PHY_CONTEXT_CMD: 4206286441Srpaulo case IWM_BINDING_CONTEXT_CMD: 4207286441Srpaulo case IWM_TIME_EVENT_CMD: 4208286441Srpaulo case IWM_SCAN_REQUEST_CMD: 4209286441Srpaulo case IWM_REPLY_BEACON_FILTERING_CMD: 4210286441Srpaulo case IWM_MAC_PM_POWER_TABLE: 4211286441Srpaulo case IWM_TIME_QUOTA_CMD: 4212286441Srpaulo case IWM_REMOVE_STA: 4213286441Srpaulo case IWM_TXPATH_FLUSH: 4214286441Srpaulo case IWM_LQ_CMD: 4215286441Srpaulo SYNC_RESP_STRUCT(cresp, pkt); 4216286441Srpaulo if (sc->sc_wantresp == ((qid << 16) | idx)) { 4217286441Srpaulo memcpy(sc->sc_cmd_resp, 4218286441Srpaulo pkt, sizeof(*pkt)+sizeof(*cresp)); 4219286441Srpaulo } 4220286441Srpaulo break; 4221286441Srpaulo 4222286441Srpaulo /* ignore */ 4223286441Srpaulo case 0x6c: /* IWM_PHY_DB_CMD, no idea why it's not in fw-api.h */ 4224286441Srpaulo break; 4225286441Srpaulo 4226286441Srpaulo case IWM_INIT_COMPLETE_NOTIF: 4227286441Srpaulo sc->sc_init_complete = 1; 4228286441Srpaulo wakeup(&sc->sc_init_complete); 4229286441Srpaulo break; 4230286441Srpaulo 4231286441Srpaulo case IWM_SCAN_COMPLETE_NOTIFICATION: { 4232286441Srpaulo struct iwm_scan_complete_notif *notif; 4233286441Srpaulo SYNC_RESP_STRUCT(notif, pkt); 4234286441Srpaulo taskqueue_enqueue(sc->sc_tq, &sc->sc_es_task); 4235286441Srpaulo break; } 4236286441Srpaulo 4237286441Srpaulo case IWM_REPLY_ERROR: { 4238286441Srpaulo struct iwm_error_resp *resp; 4239286441Srpaulo SYNC_RESP_STRUCT(resp, pkt); 4240286441Srpaulo 4241286441Srpaulo device_printf(sc->sc_dev, 4242286441Srpaulo "firmware error 0x%x, cmd 0x%x\n", 4243286441Srpaulo le32toh(resp->error_type), 4244286441Srpaulo resp->cmd_id); 4245286441Srpaulo break; } 4246286441Srpaulo 4247286441Srpaulo case IWM_TIME_EVENT_NOTIFICATION: { 4248286441Srpaulo struct iwm_time_event_notif *notif; 4249286441Srpaulo SYNC_RESP_STRUCT(notif, pkt); 4250286441Srpaulo 4251286441Srpaulo if (notif->status) { 4252286441Srpaulo if (le32toh(notif->action) & 4253286441Srpaulo IWM_TE_V2_NOTIF_HOST_EVENT_START) 4254286441Srpaulo sc->sc_auth_prot = 2; 4255286441Srpaulo else 4256286441Srpaulo sc->sc_auth_prot = 0; 4257286441Srpaulo } else { 4258286441Srpaulo sc->sc_auth_prot = -1; 4259286441Srpaulo } 4260286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 4261286441Srpaulo "%s: time event notification auth_prot=%d\n", 4262286441Srpaulo __func__, sc->sc_auth_prot); 4263286441Srpaulo 4264286441Srpaulo wakeup(&sc->sc_auth_prot); 4265286441Srpaulo break; } 4266286441Srpaulo 4267286441Srpaulo case IWM_MCAST_FILTER_CMD: 4268286441Srpaulo break; 4269286441Srpaulo 4270286441Srpaulo default: 4271286441Srpaulo device_printf(sc->sc_dev, 4272286441Srpaulo "frame %d/%d %x UNHANDLED (this should " 4273286441Srpaulo "not happen)\n", qid, idx, 4274286441Srpaulo pkt->len_n_flags); 4275286441Srpaulo break; 4276286441Srpaulo } 4277286441Srpaulo 4278286441Srpaulo /* 4279286441Srpaulo * Why test bit 0x80? The Linux driver: 4280286441Srpaulo * 4281286441Srpaulo * There is one exception: uCode sets bit 15 when it 4282286441Srpaulo * originates the response/notification, i.e. when the 4283286441Srpaulo * response/notification is not a direct response to a 4284286441Srpaulo * command sent by the driver. For example, uCode issues 4285286441Srpaulo * IWM_REPLY_RX when it sends a received frame to the driver; 4286286441Srpaulo * it is not a direct response to any driver command. 4287286441Srpaulo * 4288286441Srpaulo * Ok, so since when is 7 == 15? Well, the Linux driver 4289286441Srpaulo * uses a slightly different format for pkt->hdr, and "qid" 4290286441Srpaulo * is actually the upper byte of a two-byte field. 4291286441Srpaulo */ 4292286441Srpaulo if (!(pkt->hdr.qid & (1 << 7))) { 4293286441Srpaulo iwm_cmd_done(sc, pkt); 4294286441Srpaulo } 4295286441Srpaulo 4296286441Srpaulo ADVANCE_RXQ(sc); 4297286441Srpaulo } 4298286441Srpaulo 4299286441Srpaulo IWM_CLRBITS(sc, IWM_CSR_GP_CNTRL, 4300286441Srpaulo IWM_CSR_GP_CNTRL_REG_FLAG_MAC_ACCESS_REQ); 4301286441Srpaulo 4302286441Srpaulo /* 4303286441Srpaulo * Tell the firmware what we have processed. 4304286441Srpaulo * Seems like the hardware gets upset unless we align 4305286441Srpaulo * the write by 8?? 4306286441Srpaulo */ 4307286441Srpaulo hw = (hw == 0) ? IWM_RX_RING_COUNT - 1 : hw - 1; 4308286441Srpaulo IWM_WRITE(sc, IWM_FH_RSCSR_CHNL0_WPTR, hw & ~7); 4309286441Srpaulo} 4310286441Srpaulo 4311286441Srpaulostatic void 4312286441Srpauloiwm_intr(void *arg) 4313286441Srpaulo{ 4314286441Srpaulo struct iwm_softc *sc = arg; 4315286441Srpaulo int handled = 0; 4316286441Srpaulo int r1, r2, rv = 0; 4317286441Srpaulo int isperiodic = 0; 4318286441Srpaulo 4319286441Srpaulo IWM_LOCK(sc); 4320286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT_MASK, 0); 4321286441Srpaulo 4322286441Srpaulo if (sc->sc_flags & IWM_FLAG_USE_ICT) { 4323286441Srpaulo uint32_t *ict = sc->ict_dma.vaddr; 4324286441Srpaulo int tmp; 4325286441Srpaulo 4326286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 4327286441Srpaulo if (!tmp) 4328286441Srpaulo goto out_ena; 4329286441Srpaulo 4330286441Srpaulo /* 4331286441Srpaulo * ok, there was something. keep plowing until we have all. 4332286441Srpaulo */ 4333286441Srpaulo r1 = r2 = 0; 4334286441Srpaulo while (tmp) { 4335286441Srpaulo r1 |= tmp; 4336286441Srpaulo ict[sc->ict_cur] = 0; 4337286441Srpaulo sc->ict_cur = (sc->ict_cur+1) % IWM_ICT_COUNT; 4338286441Srpaulo tmp = htole32(ict[sc->ict_cur]); 4339286441Srpaulo } 4340286441Srpaulo 4341286441Srpaulo /* this is where the fun begins. don't ask */ 4342286441Srpaulo if (r1 == 0xffffffff) 4343286441Srpaulo r1 = 0; 4344286441Srpaulo 4345286441Srpaulo /* i am not expected to understand this */ 4346286441Srpaulo if (r1 & 0xc0000) 4347286441Srpaulo r1 |= 0x8000; 4348286441Srpaulo r1 = (0xff & r1) | ((0xff00 & r1) << 16); 4349286441Srpaulo } else { 4350286441Srpaulo r1 = IWM_READ(sc, IWM_CSR_INT); 4351286441Srpaulo /* "hardware gone" (where, fishing?) */ 4352286441Srpaulo if (r1 == 0xffffffff || (r1 & 0xfffffff0) == 0xa5a5a5a0) 4353286441Srpaulo goto out; 4354286441Srpaulo r2 = IWM_READ(sc, IWM_CSR_FH_INT_STATUS); 4355286441Srpaulo } 4356286441Srpaulo if (r1 == 0 && r2 == 0) { 4357286441Srpaulo goto out_ena; 4358286441Srpaulo } 4359286441Srpaulo 4360286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, r1 | ~sc->sc_intmask); 4361286441Srpaulo 4362286441Srpaulo /* ignored */ 4363286441Srpaulo handled |= (r1 & (IWM_CSR_INT_BIT_ALIVE /*| IWM_CSR_INT_BIT_SCD*/)); 4364286441Srpaulo 4365286441Srpaulo if (r1 & IWM_CSR_INT_BIT_SW_ERR) { 4366286441Srpaulo#ifdef IWM_DEBUG 4367286441Srpaulo int i; 4368287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4369286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4370286441Srpaulo 4371286441Srpaulo iwm_nic_error(sc); 4372286441Srpaulo 4373286441Srpaulo /* Dump driver status (TX and RX rings) while we're here. */ 4374286441Srpaulo device_printf(sc->sc_dev, "driver status:\n"); 4375286441Srpaulo for (i = 0; i < IWM_MVM_MAX_QUEUES; i++) { 4376286441Srpaulo struct iwm_tx_ring *ring = &sc->txq[i]; 4377286441Srpaulo device_printf(sc->sc_dev, 4378286441Srpaulo " tx ring %2d: qid=%-2d cur=%-3d " 4379286441Srpaulo "queued=%-3d\n", 4380286441Srpaulo i, ring->qid, ring->cur, ring->queued); 4381286441Srpaulo } 4382286441Srpaulo device_printf(sc->sc_dev, 4383286441Srpaulo " rx ring: cur=%d\n", sc->rxq.cur); 4384286441Srpaulo device_printf(sc->sc_dev, 4385286441Srpaulo " 802.11 state %d\n", vap->iv_state); 4386286441Srpaulo#endif 4387286441Srpaulo 4388286441Srpaulo device_printf(sc->sc_dev, "fatal firmware error\n"); 4389287197Sglebius iwm_stop(sc); 4390286441Srpaulo rv = 1; 4391286441Srpaulo goto out; 4392286441Srpaulo 4393286441Srpaulo } 4394286441Srpaulo 4395286441Srpaulo if (r1 & IWM_CSR_INT_BIT_HW_ERR) { 4396286441Srpaulo handled |= IWM_CSR_INT_BIT_HW_ERR; 4397286441Srpaulo device_printf(sc->sc_dev, "hardware error, stopping device\n"); 4398287197Sglebius iwm_stop(sc); 4399286441Srpaulo rv = 1; 4400286441Srpaulo goto out; 4401286441Srpaulo } 4402286441Srpaulo 4403286441Srpaulo /* firmware chunk loaded */ 4404286441Srpaulo if (r1 & IWM_CSR_INT_BIT_FH_TX) { 4405286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_TX_MASK); 4406286441Srpaulo handled |= IWM_CSR_INT_BIT_FH_TX; 4407286441Srpaulo sc->sc_fw_chunk_done = 1; 4408286441Srpaulo wakeup(&sc->sc_fw); 4409286441Srpaulo } 4410286441Srpaulo 4411286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RF_KILL) { 4412286441Srpaulo handled |= IWM_CSR_INT_BIT_RF_KILL; 4413287197Sglebius if (iwm_check_rfkill(sc)) { 4414286441Srpaulo device_printf(sc->sc_dev, 4415286441Srpaulo "%s: rfkill switch, disabling interface\n", 4416286441Srpaulo __func__); 4417287197Sglebius iwm_stop(sc); 4418286441Srpaulo } 4419286441Srpaulo } 4420286441Srpaulo 4421286441Srpaulo /* 4422286441Srpaulo * The Linux driver uses periodic interrupts to avoid races. 4423286441Srpaulo * We cargo-cult like it's going out of fashion. 4424286441Srpaulo */ 4425286441Srpaulo if (r1 & IWM_CSR_INT_BIT_RX_PERIODIC) { 4426286441Srpaulo handled |= IWM_CSR_INT_BIT_RX_PERIODIC; 4427286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, IWM_CSR_INT_BIT_RX_PERIODIC); 4428286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) == 0) 4429286441Srpaulo IWM_WRITE_1(sc, 4430286441Srpaulo IWM_CSR_INT_PERIODIC_REG, IWM_CSR_INT_PERIODIC_DIS); 4431286441Srpaulo isperiodic = 1; 4432286441Srpaulo } 4433286441Srpaulo 4434286441Srpaulo if ((r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX)) || isperiodic) { 4435286441Srpaulo handled |= (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX); 4436286441Srpaulo IWM_WRITE(sc, IWM_CSR_FH_INT_STATUS, IWM_CSR_FH_INT_RX_MASK); 4437286441Srpaulo 4438286441Srpaulo iwm_notif_intr(sc); 4439286441Srpaulo 4440286441Srpaulo /* enable periodic interrupt, see above */ 4441286441Srpaulo if (r1 & (IWM_CSR_INT_BIT_FH_RX | IWM_CSR_INT_BIT_SW_RX) && !isperiodic) 4442286441Srpaulo IWM_WRITE_1(sc, IWM_CSR_INT_PERIODIC_REG, 4443286441Srpaulo IWM_CSR_INT_PERIODIC_ENA); 4444286441Srpaulo } 4445286441Srpaulo 4446286441Srpaulo if (__predict_false(r1 & ~handled)) 4447286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_INTR, 4448286441Srpaulo "%s: unhandled interrupts: %x\n", __func__, r1); 4449286441Srpaulo rv = 1; 4450286441Srpaulo 4451286441Srpaulo out_ena: 4452286441Srpaulo iwm_restore_interrupts(sc); 4453286441Srpaulo out: 4454286441Srpaulo IWM_UNLOCK(sc); 4455286441Srpaulo return; 4456286441Srpaulo} 4457286441Srpaulo 4458286441Srpaulo/* 4459286441Srpaulo * Autoconf glue-sniffing 4460286441Srpaulo */ 4461286441Srpaulo#define PCI_VENDOR_INTEL 0x8086 4462286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_1 0x08b3 4463286441Srpaulo#define PCI_PRODUCT_INTEL_WL_3160_2 0x08b4 4464286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_1 0x08b1 4465286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7260_2 0x08b2 4466286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_1 0x095a 4467286441Srpaulo#define PCI_PRODUCT_INTEL_WL_7265_2 0x095b 4468286441Srpaulo 4469286441Srpaulostatic const struct iwm_devices { 4470286441Srpaulo uint16_t device; 4471286441Srpaulo const char *name; 4472286441Srpaulo} iwm_devices[] = { 4473286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_1, "Intel Dual Band Wireless AC 3160" }, 4474286441Srpaulo { PCI_PRODUCT_INTEL_WL_3160_2, "Intel Dual Band Wireless AC 3160" }, 4475286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_1, "Intel Dual Band Wireless AC 7260" }, 4476286441Srpaulo { PCI_PRODUCT_INTEL_WL_7260_2, "Intel Dual Band Wireless AC 7260" }, 4477286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_1, "Intel Dual Band Wireless AC 7265" }, 4478286441Srpaulo { PCI_PRODUCT_INTEL_WL_7265_2, "Intel Dual Band Wireless AC 7265" }, 4479286441Srpaulo}; 4480286441Srpaulo 4481286441Srpaulostatic int 4482286441Srpauloiwm_probe(device_t dev) 4483286441Srpaulo{ 4484286441Srpaulo int i; 4485286441Srpaulo 4486286441Srpaulo for (i = 0; i < nitems(iwm_devices); i++) 4487286441Srpaulo if (pci_get_vendor(dev) == PCI_VENDOR_INTEL && 4488286441Srpaulo pci_get_device(dev) == iwm_devices[i].device) { 4489286441Srpaulo device_set_desc(dev, iwm_devices[i].name); 4490286441Srpaulo return (BUS_PROBE_DEFAULT); 4491286441Srpaulo } 4492286441Srpaulo 4493286441Srpaulo return (ENXIO); 4494286441Srpaulo} 4495286441Srpaulo 4496286441Srpaulostatic int 4497286441Srpauloiwm_dev_check(device_t dev) 4498286441Srpaulo{ 4499286441Srpaulo struct iwm_softc *sc; 4500286441Srpaulo 4501286441Srpaulo sc = device_get_softc(dev); 4502286441Srpaulo 4503286441Srpaulo switch (pci_get_device(dev)) { 4504286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_1: 4505286441Srpaulo case PCI_PRODUCT_INTEL_WL_3160_2: 4506286441Srpaulo sc->sc_fwname = "iwm3160fw"; 4507286441Srpaulo sc->host_interrupt_operation_mode = 1; 4508286441Srpaulo return (0); 4509286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_1: 4510286441Srpaulo case PCI_PRODUCT_INTEL_WL_7260_2: 4511286441Srpaulo sc->sc_fwname = "iwm7260fw"; 4512286441Srpaulo sc->host_interrupt_operation_mode = 1; 4513286441Srpaulo return (0); 4514286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_1: 4515286441Srpaulo case PCI_PRODUCT_INTEL_WL_7265_2: 4516286441Srpaulo sc->sc_fwname = "iwm7265fw"; 4517286441Srpaulo sc->host_interrupt_operation_mode = 0; 4518286441Srpaulo return (0); 4519286441Srpaulo default: 4520286441Srpaulo device_printf(dev, "unknown adapter type\n"); 4521286441Srpaulo return ENXIO; 4522286441Srpaulo } 4523286441Srpaulo} 4524286441Srpaulo 4525286441Srpaulostatic int 4526286441Srpauloiwm_pci_attach(device_t dev) 4527286441Srpaulo{ 4528286441Srpaulo struct iwm_softc *sc; 4529286441Srpaulo int count, error, rid; 4530286441Srpaulo uint16_t reg; 4531286441Srpaulo 4532286441Srpaulo sc = device_get_softc(dev); 4533286441Srpaulo 4534286441Srpaulo /* Clear device-specific "PCI retry timeout" register (41h). */ 4535286441Srpaulo reg = pci_read_config(dev, 0x40, sizeof(reg)); 4536286441Srpaulo pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); 4537286441Srpaulo 4538286441Srpaulo /* Enable bus-mastering and hardware bug workaround. */ 4539286441Srpaulo pci_enable_busmaster(dev); 4540286441Srpaulo reg = pci_read_config(dev, PCIR_STATUS, sizeof(reg)); 4541286441Srpaulo /* if !MSI */ 4542286441Srpaulo if (reg & PCIM_STATUS_INTxSTATE) { 4543286441Srpaulo reg &= ~PCIM_STATUS_INTxSTATE; 4544286441Srpaulo } 4545286441Srpaulo pci_write_config(dev, PCIR_STATUS, reg, sizeof(reg)); 4546286441Srpaulo 4547286441Srpaulo rid = PCIR_BAR(0); 4548286441Srpaulo sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 4549286441Srpaulo RF_ACTIVE); 4550286441Srpaulo if (sc->sc_mem == NULL) { 4551286441Srpaulo device_printf(sc->sc_dev, "can't map mem space\n"); 4552286441Srpaulo return (ENXIO); 4553286441Srpaulo } 4554286441Srpaulo sc->sc_st = rman_get_bustag(sc->sc_mem); 4555286441Srpaulo sc->sc_sh = rman_get_bushandle(sc->sc_mem); 4556286441Srpaulo 4557286441Srpaulo /* Install interrupt handler. */ 4558286441Srpaulo count = 1; 4559286441Srpaulo rid = 0; 4560286441Srpaulo if (pci_alloc_msi(dev, &count) == 0) 4561286441Srpaulo rid = 1; 4562286441Srpaulo sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | 4563286441Srpaulo (rid != 0 ? 0 : RF_SHAREABLE)); 4564286441Srpaulo if (sc->sc_irq == NULL) { 4565286441Srpaulo device_printf(dev, "can't map interrupt\n"); 4566286441Srpaulo return (ENXIO); 4567286441Srpaulo } 4568286441Srpaulo error = bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE, 4569286441Srpaulo NULL, iwm_intr, sc, &sc->sc_ih); 4570286441Srpaulo if (sc->sc_ih == NULL) { 4571286441Srpaulo device_printf(dev, "can't establish interrupt"); 4572286441Srpaulo return (ENXIO); 4573286441Srpaulo } 4574286441Srpaulo sc->sc_dmat = bus_get_dma_tag(sc->sc_dev); 4575286441Srpaulo 4576286441Srpaulo return (0); 4577286441Srpaulo} 4578286441Srpaulo 4579286441Srpaulostatic void 4580286441Srpauloiwm_pci_detach(device_t dev) 4581286441Srpaulo{ 4582286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 4583286441Srpaulo 4584286441Srpaulo if (sc->sc_irq != NULL) { 4585286441Srpaulo bus_teardown_intr(dev, sc->sc_irq, sc->sc_ih); 4586286441Srpaulo bus_release_resource(dev, SYS_RES_IRQ, 4587286441Srpaulo rman_get_rid(sc->sc_irq), sc->sc_irq); 4588286441Srpaulo pci_release_msi(dev); 4589286441Srpaulo } 4590286441Srpaulo if (sc->sc_mem != NULL) 4591286441Srpaulo bus_release_resource(dev, SYS_RES_MEMORY, 4592286441Srpaulo rman_get_rid(sc->sc_mem), sc->sc_mem); 4593286441Srpaulo} 4594286441Srpaulo 4595286441Srpaulo 4596286441Srpaulo 4597286441Srpaulostatic int 4598286441Srpauloiwm_attach(device_t dev) 4599286441Srpaulo{ 4600287197Sglebius struct iwm_softc *sc = device_get_softc(dev); 4601287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4602286441Srpaulo int error; 4603286441Srpaulo int txq_i, i; 4604286441Srpaulo 4605286441Srpaulo sc->sc_dev = dev; 4606286441Srpaulo mtx_init(&sc->sc_mtx, "iwm_mtx", MTX_DEF, 0); 4607287197Sglebius mbufq_init(&sc->sc_snd, ifqmaxlen); 4608286441Srpaulo callout_init_mtx(&sc->sc_watchdog_to, &sc->sc_mtx, 0); 4609286441Srpaulo TASK_INIT(&sc->sc_es_task, 0, iwm_endscan_cb, sc); 4610286441Srpaulo sc->sc_tq = taskqueue_create("iwm_taskq", M_WAITOK, 4611286441Srpaulo taskqueue_thread_enqueue, &sc->sc_tq); 4612286441Srpaulo error = taskqueue_start_threads(&sc->sc_tq, 1, 0, "iwm_taskq"); 4613286441Srpaulo if (error != 0) { 4614286441Srpaulo device_printf(dev, "can't start threads, error %d\n", 4615286441Srpaulo error); 4616286441Srpaulo goto fail; 4617286441Srpaulo } 4618286441Srpaulo 4619286441Srpaulo /* PCI attach */ 4620286441Srpaulo error = iwm_pci_attach(dev); 4621286441Srpaulo if (error != 0) 4622286441Srpaulo goto fail; 4623286441Srpaulo 4624286441Srpaulo sc->sc_wantresp = -1; 4625286441Srpaulo 4626286441Srpaulo /* Check device type */ 4627286441Srpaulo error = iwm_dev_check(dev); 4628286441Srpaulo if (error != 0) 4629286441Srpaulo goto fail; 4630286441Srpaulo 4631286441Srpaulo sc->sc_fwdmasegsz = IWM_FWDMASEGSZ; 4632286441Srpaulo 4633286441Srpaulo /* 4634286441Srpaulo * We now start fiddling with the hardware 4635286441Srpaulo */ 4636286441Srpaulo sc->sc_hw_rev = IWM_READ(sc, IWM_CSR_HW_REV); 4637286441Srpaulo if (iwm_prepare_card_hw(sc) != 0) { 4638286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 4639286441Srpaulo goto fail; 4640286441Srpaulo } 4641286441Srpaulo 4642286441Srpaulo /* Allocate DMA memory for firmware transfers. */ 4643286441Srpaulo if ((error = iwm_alloc_fwmem(sc)) != 0) { 4644286441Srpaulo device_printf(dev, "could not allocate memory for firmware\n"); 4645286441Srpaulo goto fail; 4646286441Srpaulo } 4647286441Srpaulo 4648286441Srpaulo /* Allocate "Keep Warm" page. */ 4649286441Srpaulo if ((error = iwm_alloc_kw(sc)) != 0) { 4650286441Srpaulo device_printf(dev, "could not allocate keep warm page\n"); 4651286441Srpaulo goto fail; 4652286441Srpaulo } 4653286441Srpaulo 4654286441Srpaulo /* We use ICT interrupts */ 4655286441Srpaulo if ((error = iwm_alloc_ict(sc)) != 0) { 4656286441Srpaulo device_printf(dev, "could not allocate ICT table\n"); 4657286441Srpaulo goto fail; 4658286441Srpaulo } 4659286441Srpaulo 4660286441Srpaulo /* Allocate TX scheduler "rings". */ 4661286441Srpaulo if ((error = iwm_alloc_sched(sc)) != 0) { 4662286441Srpaulo device_printf(dev, "could not allocate TX scheduler rings\n"); 4663286441Srpaulo goto fail; 4664286441Srpaulo } 4665286441Srpaulo 4666286441Srpaulo /* Allocate TX rings */ 4667286441Srpaulo for (txq_i = 0; txq_i < nitems(sc->txq); txq_i++) { 4668286441Srpaulo if ((error = iwm_alloc_tx_ring(sc, 4669286441Srpaulo &sc->txq[txq_i], txq_i)) != 0) { 4670286441Srpaulo device_printf(dev, 4671286441Srpaulo "could not allocate TX ring %d\n", 4672286441Srpaulo txq_i); 4673286441Srpaulo goto fail; 4674286441Srpaulo } 4675286441Srpaulo } 4676286441Srpaulo 4677286441Srpaulo /* Allocate RX ring. */ 4678286441Srpaulo if ((error = iwm_alloc_rx_ring(sc, &sc->rxq)) != 0) { 4679286441Srpaulo device_printf(dev, "could not allocate RX ring\n"); 4680286441Srpaulo goto fail; 4681286441Srpaulo } 4682286441Srpaulo 4683286441Srpaulo /* Clear pending interrupts. */ 4684286441Srpaulo IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff); 4685286441Srpaulo 4686286441Srpaulo ic->ic_softc = sc; 4687286441Srpaulo ic->ic_name = device_get_nameunit(sc->sc_dev); 4688286441Srpaulo ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */ 4689286441Srpaulo ic->ic_opmode = IEEE80211_M_STA; /* default to BSS mode */ 4690286441Srpaulo 4691286441Srpaulo /* Set device capabilities. */ 4692286441Srpaulo ic->ic_caps = 4693286441Srpaulo IEEE80211_C_STA | 4694286441Srpaulo IEEE80211_C_WPA | /* WPA/RSN */ 4695286441Srpaulo IEEE80211_C_WME | 4696286441Srpaulo IEEE80211_C_SHSLOT | /* short slot time supported */ 4697286441Srpaulo IEEE80211_C_SHPREAMBLE /* short preamble supported */ 4698286441Srpaulo// IEEE80211_C_BGSCAN /* capable of bg scanning */ 4699286441Srpaulo ; 4700286441Srpaulo for (i = 0; i < nitems(sc->sc_phyctxt); i++) { 4701286441Srpaulo sc->sc_phyctxt[i].id = i; 4702286441Srpaulo sc->sc_phyctxt[i].color = 0; 4703286441Srpaulo sc->sc_phyctxt[i].ref = 0; 4704286441Srpaulo sc->sc_phyctxt[i].channel = NULL; 4705286441Srpaulo } 4706286441Srpaulo 4707286441Srpaulo /* Max RSSI */ 4708286441Srpaulo sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; 4709286441Srpaulo sc->sc_preinit_hook.ich_func = iwm_preinit; 4710286441Srpaulo sc->sc_preinit_hook.ich_arg = sc; 4711286441Srpaulo if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { 4712286441Srpaulo device_printf(dev, "config_intrhook_establish failed\n"); 4713286441Srpaulo goto fail; 4714286441Srpaulo } 4715286441Srpaulo 4716286441Srpaulo#ifdef IWM_DEBUG 4717286441Srpaulo SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), 4718286441Srpaulo SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", 4719286441Srpaulo CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); 4720286441Srpaulo#endif 4721286441Srpaulo 4722286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 4723286441Srpaulo "<-%s\n", __func__); 4724286441Srpaulo 4725286441Srpaulo return 0; 4726286441Srpaulo 4727286441Srpaulo /* Free allocated memory if something failed during attachment. */ 4728286441Srpaulofail: 4729286441Srpaulo iwm_detach_local(sc, 0); 4730286441Srpaulo 4731286441Srpaulo return ENXIO; 4732286441Srpaulo} 4733286441Srpaulo 4734286441Srpaulostatic int 4735286441Srpauloiwm_update_edca(struct ieee80211com *ic) 4736286441Srpaulo{ 4737286865Sadrian struct iwm_softc *sc = ic->ic_softc; 4738286441Srpaulo 4739286441Srpaulo device_printf(sc->sc_dev, "%s: called\n", __func__); 4740286441Srpaulo return (0); 4741286441Srpaulo} 4742286441Srpaulo 4743286441Srpaulostatic void 4744286441Srpauloiwm_preinit(void *arg) 4745286441Srpaulo{ 4746286441Srpaulo struct iwm_softc *sc = arg; 4747286441Srpaulo device_t dev = sc->sc_dev; 4748287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4749286441Srpaulo int error; 4750286441Srpaulo 4751286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 4752286441Srpaulo "->%s\n", __func__); 4753286441Srpaulo 4754286441Srpaulo IWM_LOCK(sc); 4755286441Srpaulo if ((error = iwm_start_hw(sc)) != 0) { 4756286441Srpaulo device_printf(dev, "could not initialize hardware\n"); 4757286441Srpaulo IWM_UNLOCK(sc); 4758286441Srpaulo goto fail; 4759286441Srpaulo } 4760286441Srpaulo 4761286441Srpaulo error = iwm_run_init_mvm_ucode(sc, 1); 4762286441Srpaulo iwm_stop_device(sc); 4763286441Srpaulo if (error) { 4764286441Srpaulo IWM_UNLOCK(sc); 4765286441Srpaulo goto fail; 4766286441Srpaulo } 4767286441Srpaulo device_printf(dev, 4768286441Srpaulo "revision: 0x%x, firmware %d.%d (API ver. %d)\n", 4769286441Srpaulo sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, 4770286441Srpaulo IWM_UCODE_MAJOR(sc->sc_fwver), 4771286441Srpaulo IWM_UCODE_MINOR(sc->sc_fwver), 4772286441Srpaulo IWM_UCODE_API(sc->sc_fwver)); 4773286441Srpaulo 4774286441Srpaulo /* not all hardware can do 5GHz band */ 4775286441Srpaulo if (!sc->sc_nvm.sku_cap_band_52GHz_enable) 4776286441Srpaulo memset(&ic->ic_sup_rates[IEEE80211_MODE_11A], 0, 4777286441Srpaulo sizeof(ic->ic_sup_rates[IEEE80211_MODE_11A])); 4778286441Srpaulo IWM_UNLOCK(sc); 4779286441Srpaulo 4780286441Srpaulo /* 4781286441Srpaulo * At this point we've committed - if we fail to do setup, 4782286441Srpaulo * we now also have to tear down the net80211 state. 4783286441Srpaulo */ 4784287197Sglebius ieee80211_ifattach(ic); 4785286441Srpaulo ic->ic_vap_create = iwm_vap_create; 4786286441Srpaulo ic->ic_vap_delete = iwm_vap_delete; 4787286441Srpaulo ic->ic_raw_xmit = iwm_raw_xmit; 4788286441Srpaulo ic->ic_node_alloc = iwm_node_alloc; 4789286441Srpaulo ic->ic_scan_start = iwm_scan_start; 4790286441Srpaulo ic->ic_scan_end = iwm_scan_end; 4791286441Srpaulo ic->ic_update_mcast = iwm_update_mcast; 4792286441Srpaulo ic->ic_set_channel = iwm_set_channel; 4793286441Srpaulo ic->ic_scan_curchan = iwm_scan_curchan; 4794286441Srpaulo ic->ic_scan_mindwell = iwm_scan_mindwell; 4795286441Srpaulo ic->ic_wme.wme_update = iwm_update_edca; 4796287197Sglebius ic->ic_parent = iwm_parent; 4797287197Sglebius ic->ic_transmit = iwm_transmit; 4798286441Srpaulo iwm_radiotap_attach(sc); 4799286441Srpaulo if (bootverbose) 4800286441Srpaulo ieee80211_announce(ic); 4801286441Srpaulo 4802286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 4803286441Srpaulo "<-%s\n", __func__); 4804286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 4805286441Srpaulo 4806286441Srpaulo return; 4807286441Srpaulofail: 4808286441Srpaulo config_intrhook_disestablish(&sc->sc_preinit_hook); 4809286441Srpaulo iwm_detach_local(sc, 0); 4810286441Srpaulo} 4811286441Srpaulo 4812286441Srpaulo/* 4813286441Srpaulo * Attach the interface to 802.11 radiotap. 4814286441Srpaulo */ 4815286441Srpaulostatic void 4816286441Srpauloiwm_radiotap_attach(struct iwm_softc *sc) 4817286441Srpaulo{ 4818287197Sglebius struct ieee80211com *ic = &sc->sc_ic; 4819286441Srpaulo 4820286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 4821286441Srpaulo "->%s begin\n", __func__); 4822286441Srpaulo ieee80211_radiotap_attach(ic, 4823286441Srpaulo &sc->sc_txtap.wt_ihdr, sizeof(sc->sc_txtap), 4824286441Srpaulo IWM_TX_RADIOTAP_PRESENT, 4825286441Srpaulo &sc->sc_rxtap.wr_ihdr, sizeof(sc->sc_rxtap), 4826286441Srpaulo IWM_RX_RADIOTAP_PRESENT); 4827286441Srpaulo IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, 4828286441Srpaulo "->%s end\n", __func__); 4829286441Srpaulo} 4830286441Srpaulo 4831286441Srpaulostatic struct ieee80211vap * 4832286441Srpauloiwm_vap_create(struct ieee80211com *ic, const char name[IFNAMSIZ], int unit, 4833286441Srpaulo enum ieee80211_opmode opmode, int flags, 4834286441Srpaulo const uint8_t bssid[IEEE80211_ADDR_LEN], 4835286441Srpaulo const uint8_t mac[IEEE80211_ADDR_LEN]) 4836286441Srpaulo{ 4837286441Srpaulo struct iwm_vap *ivp; 4838286441Srpaulo struct ieee80211vap *vap; 4839286441Srpaulo 4840286441Srpaulo if (!TAILQ_EMPTY(&ic->ic_vaps)) /* only one at a time */ 4841286441Srpaulo return NULL; 4842287197Sglebius ivp = malloc(sizeof(struct iwm_vap), M_80211_VAP, M_WAITOK | M_ZERO); 4843286441Srpaulo vap = &ivp->iv_vap; 4844287197Sglebius ieee80211_vap_setup(ic, vap, name, unit, opmode, flags, bssid); 4845286441Srpaulo vap->iv_bmissthreshold = 10; /* override default */ 4846286441Srpaulo /* Override with driver methods. */ 4847286441Srpaulo ivp->iv_newstate = vap->iv_newstate; 4848286441Srpaulo vap->iv_newstate = iwm_newstate; 4849286441Srpaulo 4850286441Srpaulo ieee80211_ratectl_init(vap); 4851286441Srpaulo /* Complete setup. */ 4852287197Sglebius ieee80211_vap_attach(vap, iwm_media_change, ieee80211_media_status, 4853287197Sglebius mac); 4854286441Srpaulo ic->ic_opmode = opmode; 4855286441Srpaulo 4856286441Srpaulo return vap; 4857286441Srpaulo} 4858286441Srpaulo 4859286441Srpaulostatic void 4860286441Srpauloiwm_vap_delete(struct ieee80211vap *vap) 4861286441Srpaulo{ 4862286441Srpaulo struct iwm_vap *ivp = IWM_VAP(vap); 4863286441Srpaulo 4864286441Srpaulo ieee80211_ratectl_deinit(vap); 4865286441Srpaulo ieee80211_vap_detach(vap); 4866286441Srpaulo free(ivp, M_80211_VAP); 4867286441Srpaulo} 4868286441Srpaulo 4869286441Srpaulostatic void 4870286441Srpauloiwm_scan_start(struct ieee80211com *ic) 4871286441Srpaulo{ 4872286441Srpaulo struct ieee80211vap *vap = TAILQ_FIRST(&ic->ic_vaps); 4873286865Sadrian struct iwm_softc *sc = ic->ic_softc; 4874286441Srpaulo int error; 4875286441Srpaulo 4876286441Srpaulo if (sc->sc_scanband) 4877286441Srpaulo return; 4878286441Srpaulo IWM_LOCK(sc); 4879286441Srpaulo error = iwm_mvm_scan_request(sc, IEEE80211_CHAN_2GHZ, 0, NULL, 0); 4880286441Srpaulo if (error) { 4881286441Srpaulo device_printf(sc->sc_dev, "could not initiate scan\n"); 4882286441Srpaulo IWM_UNLOCK(sc); 4883286441Srpaulo ieee80211_cancel_scan(vap); 4884286441Srpaulo } else 4885286441Srpaulo IWM_UNLOCK(sc); 4886286441Srpaulo} 4887286441Srpaulo 4888286441Srpaulostatic void 4889286441Srpauloiwm_scan_end(struct ieee80211com *ic) 4890286441Srpaulo{ 4891286441Srpaulo} 4892286441Srpaulo 4893286441Srpaulostatic void 4894286441Srpauloiwm_update_mcast(struct ieee80211com *ic) 4895286441Srpaulo{ 4896286441Srpaulo} 4897286441Srpaulo 4898286441Srpaulostatic void 4899286441Srpauloiwm_set_channel(struct ieee80211com *ic) 4900286441Srpaulo{ 4901286441Srpaulo} 4902286441Srpaulo 4903286441Srpaulostatic void 4904286441Srpauloiwm_scan_curchan(struct ieee80211_scan_state *ss, unsigned long maxdwell) 4905286441Srpaulo{ 4906286441Srpaulo} 4907286441Srpaulo 4908286441Srpaulostatic void 4909286441Srpauloiwm_scan_mindwell(struct ieee80211_scan_state *ss) 4910286441Srpaulo{ 4911286441Srpaulo return; 4912286441Srpaulo} 4913286441Srpaulo 4914286441Srpaulovoid 4915286441Srpauloiwm_init_task(void *arg1) 4916286441Srpaulo{ 4917286441Srpaulo struct iwm_softc *sc = arg1; 4918286441Srpaulo 4919286441Srpaulo IWM_LOCK(sc); 4920286441Srpaulo while (sc->sc_flags & IWM_FLAG_BUSY) 4921286441Srpaulo msleep(&sc->sc_flags, &sc->sc_mtx, 0, "iwmpwr", 0); 4922286441Srpaulo sc->sc_flags |= IWM_FLAG_BUSY; 4923287197Sglebius iwm_stop(sc); 4924287197Sglebius if (sc->sc_ic.ic_nrunning > 0) 4925286441Srpaulo iwm_init(sc); 4926286441Srpaulo sc->sc_flags &= ~IWM_FLAG_BUSY; 4927286441Srpaulo wakeup(&sc->sc_flags); 4928286441Srpaulo IWM_UNLOCK(sc); 4929286441Srpaulo} 4930286441Srpaulo 4931286441Srpaulostatic int 4932286441Srpauloiwm_resume(device_t dev) 4933286441Srpaulo{ 4934286441Srpaulo uint16_t reg; 4935286441Srpaulo 4936286441Srpaulo /* Clear device-specific "PCI retry timeout" register (41h). */ 4937286441Srpaulo reg = pci_read_config(dev, 0x40, sizeof(reg)); 4938286441Srpaulo pci_write_config(dev, 0x40, reg & ~0xff00, sizeof(reg)); 4939286441Srpaulo iwm_init_task(device_get_softc(dev)); 4940286441Srpaulo 4941286441Srpaulo return 0; 4942286441Srpaulo} 4943286441Srpaulo 4944286441Srpaulostatic int 4945286441Srpauloiwm_suspend(device_t dev) 4946286441Srpaulo{ 4947286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 4948286441Srpaulo 4949287197Sglebius if (sc->sc_ic.ic_nrunning > 0) { 4950287197Sglebius IWM_LOCK(sc); 4951287197Sglebius iwm_stop(sc); 4952287197Sglebius IWM_UNLOCK(sc); 4953287197Sglebius } 4954286441Srpaulo 4955286441Srpaulo return (0); 4956286441Srpaulo} 4957286441Srpaulo 4958286441Srpaulostatic int 4959286441Srpauloiwm_detach_local(struct iwm_softc *sc, int do_net80211) 4960286441Srpaulo{ 4961286441Srpaulo struct iwm_fw_info *fw = &sc->sc_fw; 4962286441Srpaulo device_t dev = sc->sc_dev; 4963286441Srpaulo int i; 4964286441Srpaulo 4965286441Srpaulo if (sc->sc_tq) { 4966286441Srpaulo taskqueue_drain_all(sc->sc_tq); 4967286441Srpaulo taskqueue_free(sc->sc_tq); 4968286441Srpaulo } 4969287197Sglebius callout_drain(&sc->sc_watchdog_to); 4970287197Sglebius iwm_stop_device(sc); 4971287197Sglebius if (do_net80211) 4972287197Sglebius ieee80211_ifdetach(&sc->sc_ic); 4973286441Srpaulo 4974286441Srpaulo /* Free descriptor rings */ 4975286441Srpaulo for (i = 0; i < nitems(sc->txq); i++) 4976286441Srpaulo iwm_free_tx_ring(sc, &sc->txq[i]); 4977286441Srpaulo 4978286441Srpaulo /* Free firmware */ 4979286441Srpaulo if (fw->fw_rawdata != NULL) 4980286441Srpaulo iwm_fw_info_free(fw); 4981286441Srpaulo 4982286441Srpaulo /* free scheduler */ 4983286441Srpaulo iwm_free_sched(sc); 4984286441Srpaulo if (sc->ict_dma.vaddr != NULL) 4985286441Srpaulo iwm_free_ict(sc); 4986286441Srpaulo if (sc->kw_dma.vaddr != NULL) 4987286441Srpaulo iwm_free_kw(sc); 4988286441Srpaulo if (sc->fw_dma.vaddr != NULL) 4989286441Srpaulo iwm_free_fwmem(sc); 4990286441Srpaulo 4991286441Srpaulo /* Finished with the hardware - detach things */ 4992286441Srpaulo iwm_pci_detach(dev); 4993286441Srpaulo 4994287197Sglebius mbufq_drain(&sc->sc_snd); 4995286441Srpaulo mtx_destroy(&sc->sc_mtx); 4996286441Srpaulo 4997286441Srpaulo return (0); 4998286441Srpaulo} 4999286441Srpaulo 5000286441Srpaulostatic int 5001286441Srpauloiwm_detach(device_t dev) 5002286441Srpaulo{ 5003286441Srpaulo struct iwm_softc *sc = device_get_softc(dev); 5004286441Srpaulo 5005286441Srpaulo return (iwm_detach_local(sc, 1)); 5006286441Srpaulo} 5007286441Srpaulo 5008286441Srpaulostatic device_method_t iwm_pci_methods[] = { 5009286441Srpaulo /* Device interface */ 5010286441Srpaulo DEVMETHOD(device_probe, iwm_probe), 5011286441Srpaulo DEVMETHOD(device_attach, iwm_attach), 5012286441Srpaulo DEVMETHOD(device_detach, iwm_detach), 5013286441Srpaulo DEVMETHOD(device_suspend, iwm_suspend), 5014286441Srpaulo DEVMETHOD(device_resume, iwm_resume), 5015286441Srpaulo 5016286441Srpaulo DEVMETHOD_END 5017286441Srpaulo}; 5018286441Srpaulo 5019286441Srpaulostatic driver_t iwm_pci_driver = { 5020286441Srpaulo "iwm", 5021286441Srpaulo iwm_pci_methods, 5022286441Srpaulo sizeof (struct iwm_softc) 5023286441Srpaulo}; 5024286441Srpaulo 5025286441Srpaulostatic devclass_t iwm_devclass; 5026286441Srpaulo 5027286441SrpauloDRIVER_MODULE(iwm, pci, iwm_pci_driver, iwm_devclass, NULL, NULL); 5028286441SrpauloMODULE_DEPEND(iwm, firmware, 1, 1, 1); 5029286441SrpauloMODULE_DEPEND(iwm, pci, 1, 1, 1); 5030286441SrpauloMODULE_DEPEND(iwm, wlan, 1, 1, 1); 5031