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