diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/pci/if_iwm.c | 1539 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmreg.h | 998 | ||||
-rw-r--r-- | sys/dev/pci/if_iwmvar.h | 25 |
3 files changed, 1933 insertions, 629 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index c8f37783a4f..177f22f626b 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,7 +1,8 @@ -/* $OpenBSD: if_iwm.c,v 1.81 2016/05/18 07:28:01 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.82 2016/05/25 13:35:12 stsp Exp $ */ /* - * Copyright (c) 2014 genua mbh <info@genua.de> + * Copyright (c) 2014, 2016 genua gmbh <info@genua.de> + * Author: Stefan Sperling <stsp@openbsd.org> * Copyright (c) 2014 Fixup Software Ltd. * * Permission to use, copy, modify, and distribute this software for any @@ -21,9 +22,6 @@ * Based on BSD-licensed source modules in the Linux iwlwifi driver, * which were used as the reference documentation for this implementation. * - * Driver version we are currently based off of is - * Linux 3.14.3 (tag id a2df521e42b1d9a23f620ac79dbfe8655a8391dd) - * *********************************************************************** * * This file is provided under a dual BSD/GPLv2 license. When using or @@ -32,6 +30,8 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2007 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -58,6 +58,8 @@ * BSD LICENSE * * Copyright(c) 2005 - 2013 Intel Corporation. All rights reserved. + * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH + * Copyright(c) 2016 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -129,6 +131,7 @@ #include <net/bpf.h> #endif #include <net/if.h> +#include <net/if_dl.h> #include <net/if_media.h> #include <netinet/in.h> @@ -205,6 +208,11 @@ const int iwm_mcs2ridx[] = { IWM_RATE_MCS_7_INDEX, }; +struct iwm_nvm_section { + uint16_t length; + uint8_t *data; +}; + int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t); int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type, uint8_t *, size_t); @@ -260,7 +268,7 @@ void iwm_mvm_nic_config(struct iwm_softc *); int iwm_nic_rx_init(struct iwm_softc *); int iwm_nic_tx_init(struct iwm_softc *); int iwm_nic_init(struct iwm_softc *); -void iwm_enable_txq(struct iwm_softc *, int, int); +int iwm_enable_txq(struct iwm_softc *, int, int, int); int iwm_post_alive(struct iwm_softc *); struct iwm_phy_db_entry *iwm_phy_db_get_section(struct iwm_softc *, enum iwm_phy_db_section_type, uint16_t); @@ -287,8 +295,9 @@ void iwm_mvm_protect_session(struct iwm_softc *, struct iwm_node *, int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, uint16_t, uint8_t *, uint16_t *); int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *, - uint16_t *); -void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const); + uint16_t *, size_t); +void iwm_init_channel_map(struct iwm_softc *, const uint16_t * const, + const uint8_t *nvm_channels, size_t nchan); void iwm_setup_ht_rates(struct iwm_softc *); void iwm_htprot_task(void *); void iwm_update_htprot(struct ieee80211com *, struct ieee80211_node *); @@ -307,14 +316,16 @@ void iwm_ampdu_tx_stop(struct ieee80211com *, void iwm_ba_task(void *); int iwm_parse_nvm_data(struct iwm_softc *, const uint16_t *, - const uint16_t *, const uint16_t *, uint8_t, - uint8_t); -#ifdef notyet + const uint16_t *, const uint16_t *, + const uint16_t *, const uint16_t *, + const uint16_t *); int iwm_parse_nvm_sections(struct iwm_softc *, struct iwm_nvm_section *); -#endif int iwm_nvm_init(struct iwm_softc *); +int iwm_firmware_load_sect(struct iwm_softc *, uint32_t, const uint8_t *, + uint32_t); int iwm_firmware_load_chunk(struct iwm_softc *, uint32_t, const uint8_t *, uint32_t); +int iwm_load_firmware_7000(struct iwm_softc *, enum iwm_ucode_type); int iwm_load_firmware(struct iwm_softc *, enum iwm_ucode_type); int iwm_start_fw(struct iwm_softc *, enum iwm_ucode_type); int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); @@ -335,7 +346,7 @@ void iwm_mvm_rx_tx_cmd_single(struct iwm_softc *, struct iwm_rx_packet *, void iwm_mvm_rx_tx_cmd(struct iwm_softc *, struct iwm_rx_packet *, struct iwm_rx_data *); int iwm_mvm_binding_cmd(struct iwm_softc *, struct iwm_node *, uint32_t); -int iwm_mvm_binding_update(struct iwm_softc *, struct iwm_node *, int); +int iwm_mvm_binding_update(struct iwm_softc *, struct iwm_node *); int iwm_mvm_binding_add_vif(struct iwm_softc *, struct iwm_node *); void iwm_mvm_phy_ctxt_cmd_hdr(struct iwm_softc *, struct iwm_mvm_phy_ctxt *, struct iwm_phy_context_cmd *, uint32_t, uint32_t); @@ -378,11 +389,9 @@ void iwm_mvm_power_build_cmd(struct iwm_softc *, struct iwm_node *, int iwm_mvm_power_mac_update_mode(struct iwm_softc *, struct iwm_node *); int iwm_mvm_power_update_device(struct iwm_softc *); int iwm_mvm_enable_beacon_filter(struct iwm_softc *, struct iwm_node *); -int iwm_mvm_disable_beacon_filter(struct iwm_softc *, struct iwm_node *); -void iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *, - struct iwm_mvm_add_sta_cmd_v5 *); +int iwm_mvm_disable_beacon_filter(struct iwm_softc *); int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *, - struct iwm_mvm_add_sta_cmd_v6 *, int *); + struct iwm_mvm_add_sta_cmd_v7 *, int *); int iwm_mvm_sta_send_to_fw(struct iwm_softc *, struct iwm_node *, int); int iwm_mvm_add_sta(struct iwm_softc *, struct iwm_node *); int iwm_mvm_update_sta(struct iwm_softc *, struct iwm_node *); @@ -392,15 +401,13 @@ int iwm_mvm_add_aux_sta(struct iwm_softc *); uint16_t iwm_mvm_scan_rx_chain(struct iwm_softc *); uint32_t iwm_mvm_scan_max_out_time(struct iwm_softc *, uint32_t, int); uint32_t iwm_mvm_scan_suspend_time(struct iwm_softc *, int); -uint32_t iwm_mvm_scan_rxon_flags(struct iwm_softc *, int); uint32_t iwm_mvm_scan_rate_n_flags(struct iwm_softc *, int, int); uint16_t iwm_mvm_get_active_dwell(struct iwm_softc *, int, int); uint16_t iwm_mvm_get_passive_dwell(struct iwm_softc *, int); -int iwm_mvm_scan_fill_channels(struct iwm_softc *, struct iwm_scan_cmd *, - int, int, int); -uint16_t iwm_mvm_fill_probe_req(struct iwm_softc *, struct ieee80211_frame *, - const uint8_t *, int, const uint8_t *, int, const uint8_t *, int, int); -int iwm_mvm_scan_request(struct iwm_softc *, int, int, uint8_t *, int); +uint8_t iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *, + struct iwm_scan_channel_cfg_lmac *, int); +int iwm_mvm_fill_probe_req(struct iwm_softc *, struct iwm_scan_probe_req *); +int iwm_mvm_lmac_scan(struct iwm_softc *); void iwm_mvm_ack_rates(struct iwm_softc *, struct iwm_node *, int *, int *); void iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *, struct iwm_node *, struct iwm_mac_ctx_cmd *, uint32_t); @@ -423,6 +430,12 @@ int iwm_media_change(struct ifnet *); void iwm_newstate_task(void *); int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int); void iwm_endscan_cb(void *); +void iwm_mvm_fill_sf_command(struct iwm_softc *, struct iwm_sf_cfg_cmd *, + struct ieee80211_node *); +int iwm_mvm_sf_config(struct iwm_softc *, enum iwm_sf_state); +int iwm_send_bt_init_conf(struct iwm_softc *); +int iwm_send_update_mcc_cmd(struct iwm_softc *, const char *); +void iwm_mvm_tt_tx_backoff(struct iwm_softc *, uint32_t); int iwm_init_hw(struct iwm_softc *); int iwm_init(struct ifnet *); void iwm_start(struct ifnet *); @@ -565,6 +578,12 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) goto out; } + /* (Re-)Initialize default values. */ + sc->sc_capaflags = 0; + sc->sc_capa_n_scan_channels = IWM_MAX_NUM_SCAN_CHANNELS; + memset(sc->sc_enabled_capa, 0, sizeof(sc->sc_enabled_capa)); + memset(sc->sc_fw_mcc, 0, sizeof(sc->sc_fw_mcc)); + /* * Parse firmware contents */ @@ -578,7 +597,10 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) goto out; } - sc->sc_fwver = le32toh(uhdr->ver); + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), "%d.%d (API ver %d)", + IWM_UCODE_MAJOR(le32toh(uhdr->ver)), + IWM_UCODE_MINOR(le32toh(uhdr->ver)), + IWM_UCODE_API(le32toh(uhdr->ver))); data = uhdr->data; len = fw->fw_rawsize - sizeof(*uhdr); @@ -610,7 +632,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) sc->sc_capa_max_probe_len = le32toh(*(uint32_t *)tlv_data); /* limit it to something sensible */ - if (sc->sc_capa_max_probe_len > (1<<16)) { + if (sc->sc_capa_max_probe_len > + IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE) { DPRINTF(("%s: IWM_UCODE_TLV_PROBE_MAX_LEN " "ridiculous\n", DEVNAME(sc))); error = EINVAL; @@ -690,11 +713,79 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) sc->sc_fw_phy_config = le32toh(*(uint32_t *)tlv_data); break; - case IWM_UCODE_TLV_API_CHANGES_SET: - case IWM_UCODE_TLV_ENABLED_CAPABILITIES: + case IWM_UCODE_TLV_API_CHANGES_SET: { + struct iwm_ucode_api *api; + if (tlv_len != sizeof(*api)) { + error = EINVAL; + goto parse_out; + } + api = (struct iwm_ucode_api *)tlv_data; + /* Flags may exceed 32 bits in future firmware. */ + if (le32toh(api->api_index) > 0) { + DPRINTF(("%s: unsupported API index %d\n", + DEVNAME(sc), le32toh(api->api_index))); + goto parse_out; + } + sc->sc_ucode_api = le32toh(api->api_flags); + break; + } + + case IWM_UCODE_TLV_ENABLED_CAPABILITIES: { + struct iwm_ucode_capa *capa; + int idx, i; + if (tlv_len != sizeof(*capa)) { + error = EINVAL; + goto parse_out; + } + capa = (struct iwm_ucode_capa *)tlv_data; + idx = le32toh(capa->api_index); + if (idx > howmany(IWM_NUM_UCODE_TLV_CAPA, 32)) { + DPRINTF(("%s: unsupported API index %d\n", + DEVNAME(sc), idx)); + goto parse_out; + } + for (i = 0; i < 32; i++) { + if ((le32toh(capa->api_capa) & (1 << i)) == 0) + continue; + setbit(sc->sc_enabled_capa, i + (32 * idx)); + } + break; + } + + case 48: /* undocumented TLV */ + case IWM_UCODE_TLV_SDIO_ADMA_ADDR: + case IWM_UCODE_TLV_FW_GSCAN_CAPA: /* ignore, not used by current driver */ break; + case IWM_UCODE_TLV_SEC_RT_USNIFFER: + if ((error = iwm_firmware_store_section(sc, + IWM_UCODE_TYPE_REGULAR_USNIFFER, tlv_data, + tlv_len)) != 0) + goto parse_out; + break; + + case IWM_UCODE_TLV_N_SCAN_CHANNELS: + if (tlv_len != sizeof(uint32_t)) { + error = EINVAL; + goto parse_out; + } + sc->sc_capa_n_scan_channels = + le32toh(*(uint32_t *)tlv_data); + break; + + case IWM_UCODE_TLV_FW_VERSION: + if (tlv_len != sizeof(uint32_t) * 3) { + error = EINVAL; + goto parse_out; + } + snprintf(sc->sc_fwver, sizeof(sc->sc_fwver), + "%d.%d.%d", + le32toh(((uint32_t *)tlv_data)[0]), + le32toh(((uint32_t *)tlv_data)[1]), + le32toh(((uint32_t *)tlv_data)[2])); + break; + default: DPRINTF(("%s: unknown firmware section %d, abort\n", DEVNAME(sc), tlv_type)); @@ -1291,6 +1382,7 @@ iwm_ict_reset(struct iwm_softc *sc) IWM_WRITE(sc, IWM_CSR_DRAM_INT_TBL_REG, IWM_CSR_DRAM_INT_TBL_ENABLE | IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK + | IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER | sc->ict_dma.paddr >> IWM_ICT_PADDR_SHIFT); /* Switch to ICT interrupt mode in driver. */ @@ -1305,13 +1397,20 @@ iwm_ict_reset(struct iwm_softc *sc) int iwm_set_hw_ready(struct iwm_softc *sc) { + int ready; + IWM_SETBITS(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY); - return iwm_poll_bit(sc, IWM_CSR_HW_IF_CONFIG_REG, + ready = iwm_poll_bit(sc, IWM_CSR_HW_IF_CONFIG_REG, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY, IWM_HW_READY_TIMEOUT); + if (ready) + IWM_SETBITS(sc, IWM_CSR_MBOX_SET_REG, + IWM_CSR_MBOX_SET_REG_OS_ALIVE); + + return ready; } #undef IWM_HW_READY_TIMEOUT @@ -1488,9 +1587,7 @@ iwm_start_hw(struct iwm_softc *sc) return error; /* Reset the entire device */ - IWM_WRITE(sc, IWM_CSR_RESET, - IWM_CSR_RESET_REG_FLAG_SW_RESET | - IWM_CSR_RESET_REG_FLAG_NEVO_RESET); + IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); DELAY(10); if ((error = iwm_apm_init(sc)) != 0) @@ -1563,7 +1660,7 @@ iwm_stop_device(struct iwm_softc *sc) */ iwm_disable_interrupts(sc); /* stop and reset the on-board processor */ - IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_NEVO_RESET); + IWM_WRITE(sc, IWM_CSR_RESET, IWM_CSR_RESET_REG_FLAG_SW_RESET); /* * Even if we stop the HW, we still want the RF kill @@ -1646,16 +1743,12 @@ iwm_nic_rx_init(struct iwm_softc *sc) IWM_FH_RSCSR_CHNL0_STTS_WPTR_REG, sc->rxq.stat_dma.paddr >> 4); /* Enable RX. */ - /* - * Note: Linux driver also sets this: - * (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | - * - * It causes weird behavior. YMMV. - */ IWM_WRITE(sc, IWM_FH_MEM_RCSR_CHNL0_CONFIG_REG, IWM_FH_RCSR_RX_CONFIG_CHNL_EN_ENABLE_VAL | IWM_FH_RCSR_CHNL0_RX_IGNORE_RXF_EMPTY | /* HW bug */ IWM_FH_RCSR_CHNL0_RX_CONFIG_IRQ_DEST_INT_HOST_VAL | + IWM_FH_RCSR_CHNL0_RX_CONFIG_SINGLE_FRAME_MSK | + (IWM_RX_RB_TIMEOUT << IWM_FH_RCSR_RX_CONFIG_REG_IRQ_RBTH_POS) | IWM_FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K | IWM_RX_QUEUE_SIZE_LOG << IWM_FH_RCSR_RX_CONFIG_RBDCB_SIZE_POS); @@ -1702,6 +1795,9 @@ iwm_nic_tx_init(struct iwm_softc *sc) DPRINTF(("loading ring %d descriptors (%p) at %lx\n", qid, txq->desc, txq->desc_dma.paddr >> 8)); } + + iwm_write_prph(sc, IWM_SCD_GP_CTRL, IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE); + iwm_nic_unlock(sc); return 0; @@ -1739,47 +1835,74 @@ const uint8_t iwm_mvm_ac_to_tx_fifo[] = { IWM_MVM_TX_FIFO_BK, }; -void -iwm_enable_txq(struct iwm_softc *sc, int qid, int fifo) +int +iwm_enable_txq(struct iwm_softc *sc, int sta_id, int qid, int fifo) { if (!iwm_nic_lock(sc)) { DPRINTF(("%s: cannot enable txq %d\n", DEVNAME(sc), qid)); - return; /* XXX return EBUSY */ + return EBUSY; } - /* unactivate before configuration */ - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) - | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - if (qid != IWM_MVM_CMD_QUEUE) { - iwm_set_bits_prph(sc, IWM_SCD_QUEUECHAIN_SEL, (1 << qid)); - } + if (qid == IWM_MVM_CMD_QUEUE) { + /* unactivate before configuration */ + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (0 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) + | (1 << IWM_SCD_QUEUE_STTS_REG_POS_SCD_ACT_EN)); + + iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + + iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); + + iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); + /* Set scheduler window size and frame limit. */ + iwm_write_mem32(sc, + sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + + sizeof(uint32_t), + ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & + IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | + ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & + IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); + + iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), + (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | + (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | + (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | + IWM_SCD_QUEUE_STTS_REG_MSK); + } else { + struct iwm_scd_txq_cfg_cmd cmd; + int error; - iwm_clear_bits_prph(sc, IWM_SCD_AGGR_SEL, (1 << qid)); + iwm_nic_unlock(sc); - IWM_WRITE(sc, IWM_HBUS_TARG_WRPTR, qid << 8 | 0); - iwm_write_prph(sc, IWM_SCD_QUEUE_RDPTR(qid), 0); - - iwm_write_mem32(sc, sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid), 0); - /* Set scheduler window size and frame limit. */ - iwm_write_mem32(sc, - sc->sched_base + IWM_SCD_CONTEXT_QUEUE_OFFSET(qid) + - sizeof(uint32_t), - ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_POS) & - IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK) | - ((IWM_FRAME_LIMIT << IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS) & - IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK)); - - iwm_write_prph(sc, IWM_SCD_QUEUE_STATUS_BITS(qid), - (1 << IWM_SCD_QUEUE_STTS_REG_POS_ACTIVE) | - (fifo << IWM_SCD_QUEUE_STTS_REG_POS_TXF) | - (1 << IWM_SCD_QUEUE_STTS_REG_POS_WSL) | - IWM_SCD_QUEUE_STTS_REG_MSK); + memset(&cmd, 0, sizeof(cmd)); + cmd.scd_queue = qid; + cmd.enable = 1; + cmd.sta_id = sta_id; + cmd.tx_fifo = fifo; + cmd.aggregate = 0; + cmd.window = IWM_FRAME_LIMIT; + + error = iwm_mvm_send_cmd_pdu(sc, IWM_SCD_QUEUE_CFG, IWM_CMD_SYNC, + sizeof(cmd), &cmd); + if (error) { + DPRINTF(("%s: cannot enable txq %d\n", DEVNAME(sc), qid)); + return error; + } + + if (!iwm_nic_lock(sc)) + return EBUSY; + } + + iwm_write_prph(sc, IWM_SCD_EN_CTRL, + iwm_read_prph(sc, IWM_SCD_EN_CTRL) | qid); iwm_nic_unlock(sc); DPRINTF(("enabled txq %d FIFO %d\n", qid, fifo)); + + return 0; } int @@ -1787,15 +1910,15 @@ iwm_post_alive(struct iwm_softc *sc) { int nwords; int error, chnl; + uint32_t base; if (!iwm_nic_lock(sc)) return EBUSY; - if (sc->sched_base != iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR)) { - DPRINTF(("%s: sched addr mismatch", DEVNAME(sc))); - error = EINVAL; - goto out; - } + base = iwm_read_prph(sc, IWM_SCD_SRAM_BASE_ADDR); + if (sc->sched_base != base) + DPRINTF(("%s: sched addr mismatch: alive: 0x%x prph: 0x%x\n", + DEVNAME(sc), sc->sched_base, base)); iwm_ict_reset(sc); @@ -1814,8 +1937,15 @@ iwm_post_alive(struct iwm_softc *sc) iwm_write_prph(sc, IWM_SCD_CHAINEXT_EN, 0); + iwm_nic_unlock(sc); + /* enable command channel */ - iwm_enable_txq(sc, IWM_MVM_CMD_QUEUE, 7); + error = iwm_enable_txq(sc, 0 /* unused */, IWM_MVM_CMD_QUEUE, 7); + if (error) + return error; + + if (!iwm_nic_lock(sc)) + return EBUSY; iwm_write_prph(sc, IWM_SCD_TXFACT, 0xff); @@ -2017,7 +2147,6 @@ iwm_send_phy_db_cmd(struct iwm_softc *sc, uint16_t type, cmd.len[0] = sizeof(struct iwm_phy_db_cmd); cmd.data[1] = data; cmd.len[1] = length; - cmd.dataflags[1] = IWM_HCMD_DFL_NOCOPY; return iwm_send_cmd(sc, &cmd); } @@ -2047,6 +2176,7 @@ iwm_phy_db_send_all_channel_groups(struct iwm_softc *sc, return err; } + DELAY(1000); DPRINTFN(10, ("Sent PHY_DB HCMD, type = %d num = %d\n", type, i)); } @@ -2212,8 +2342,7 @@ iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_node *in, htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); time_cmd.id = htole32(IWM_TE_BSS_STA_AGGRESSIVE_ASSOC); - time_cmd.apply_time = htole32(iwm_read_prph(sc, - IWM_DEVICE_SYSTEM_TIME_REG)); + time_cmd.apply_time = htole32(0); time_cmd.max_frags = IWM_TE_V2_FRAG_NONE; time_cmd.max_delay = htole32(max_delay); @@ -2223,7 +2352,8 @@ iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_node *in, time_cmd.repeat = 1; time_cmd.policy = htole32(IWM_TE_V2_NOTIF_HOST_EVENT_START | - IWM_TE_V2_NOTIF_HOST_EVENT_END); + IWM_TE_V2_NOTIF_HOST_EVENT_END | + IWM_T2_V2_START_IMMEDIATELY); iwm_mvm_time_event_send_add(sc, in, /*te_data*/NULL, &time_cmd); } @@ -2237,17 +2367,22 @@ iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_node *in, const int nvm_to_read[] = { IWM_NVM_SECTION_TYPE_HW, IWM_NVM_SECTION_TYPE_SW, + IWM_NVM_SECTION_TYPE_REGULATORY, IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, }; /* Default NVM size to read */ -#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) -#define IWM_MAX_NVM_SECTION_SIZE 7000 +#define IWM_NVM_DEFAULT_CHUNK_SIZE (2*1024) +#define IWM_MAX_NVM_SECTION_SIZE 8192 #define IWM_NVM_WRITE_OPCODE 1 #define IWM_NVM_READ_OPCODE 0 +/* load nvm chunk response */ +#define IWM_READ_NVM_CHUNK_SUCCEED 0 +#define IWM_READ_NVM_CHUNK_INVALID_ADDRESS 1 + int iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, uint16_t offset, uint16_t length, uint8_t *data, uint16_t *len) @@ -2267,14 +2402,18 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, IWM_CMD_SEND_IN_RFKILL, .data = { &nvm_access_cmd, }, }; - int ret, bytes_read, offset_read; + int ret, offset_read; + size_t bytes_read; uint8_t *resp_data; cmd.len[0] = sizeof(struct iwm_nvm_access_cmd); ret = iwm_send_cmd(sc, &cmd); - if (ret) + if (ret) { + DPRINTF(("%s: Could not send NVM_ACCESS command (error=%d)\n", + DEVNAME(sc), ret)); return ret; + } pkt = cmd.resp_pkt; if (pkt->hdr.flags & IWM_CMD_FAILED_MSK) { @@ -2305,6 +2444,14 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, goto exit; } + if (bytes_read > length) { + DPRINTF(("%s: NVM ACCESS response with too much data " + "(%d bytes requested, %zd bytes received)\n", + DEVNAME(sc), length, bytes_read)); + ret = EINVAL; + goto exit; + } + memcpy(data + offset, resp_data, bytes_read); *len = bytes_read; @@ -2325,45 +2472,63 @@ iwm_nvm_read_chunk(struct iwm_softc *sc, uint16_t section, */ int iwm_nvm_read_section(struct iwm_softc *sc, - uint16_t section, uint8_t *data, uint16_t *len) + uint16_t section, uint8_t *data, uint16_t *len, size_t max_len) { - uint16_t length, seglen; - int error; + uint16_t chunklen, seglen; + int error = 0; - /* Set nvm section read length */ - length = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; + DPRINTFN(2, ("%s: reading NVM section %d\n", DEVNAME(sc), section)); + + chunklen = seglen = IWM_NVM_DEFAULT_CHUNK_SIZE; *len = 0; - /* Read the NVM until exhausted (reading less than requested) */ - while (seglen == length) { + /* Read NVM chunks until exhausted (reading less than requested) */ + while (seglen == chunklen && *len < max_len) { error = iwm_nvm_read_chunk(sc, - section, *len, length, data, &seglen); + section, *len, chunklen, data, &seglen); if (error) { - printf("%s: Cannot read NVM from section " - "%d offset %d, length %d\n", - DEVNAME(sc), section, *len, length); + DPRINTF(("%s: Cannot read from NVM section " + "%d at offset %d\n", DEVNAME(sc), section, *len)); return error; } *len += seglen; } - DPRINTFN(4, ("NVM section %d read completed\n", section)); - return 0; + DPRINTFN(2, ("%s: NVM section %d read completed (%d bytes, error=%d)\n", + DEVNAME(sc), section, *len, error)); + return error; } -/* - * BEGIN IWM_NVM_PARSE - */ +uint8_t +iwm_fw_valid_tx_ant(struct iwm_softc *sc) +{ + uint8_t tx_ant; + + tx_ant = ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) + >> IWM_FW_PHY_CFG_TX_CHAIN_POS); + + if (sc->sc_nvm.valid_tx_ant) + tx_ant &= sc->sc_nvm.valid_tx_ant; + + return tx_ant; +} + +uint8_t +iwm_fw_valid_rx_ant(struct iwm_softc *sc) +{ + uint8_t rx_ant; + + rx_ant = ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) + >> IWM_FW_PHY_CFG_RX_CHAIN_POS); -#define IWM_FW_VALID_TX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_TX_CHAIN) \ - >> IWM_FW_PHY_CFG_TX_CHAIN_POS) -#define IWM_FW_VALID_RX_ANT(sc) \ - ((sc->sc_fw_phy_config & IWM_FW_PHY_CFG_RX_CHAIN) \ - >> IWM_FW_PHY_CFG_RX_CHAIN_POS) + if (sc->sc_nvm.valid_rx_ant) + rx_ant &= sc->sc_nvm.valid_rx_ant; + + return rx_ant; +} /* NVM offsets (in words) definitions */ -enum wkp_nvm_offsets { +enum iwm_nvm_offsets { /* NVM HW-Section offset (in words) definitions */ IWM_HW_ADDR = 0x15, @@ -2424,7 +2589,8 @@ enum iwm_nvm_channel_flags { }; void -iwm_init_channel_map(struct iwm_softc *sc, const uint16_t * const nvm_ch_flags) +iwm_init_channel_map(struct iwm_softc *sc, const uint16_t * const nvm_ch_flags, + const uint8_t *nvm_channels, size_t nchan) { struct ieee80211com *ic = &sc->sc_ic; struct iwm_nvm_data *data = &sc->sc_nvm; @@ -2434,7 +2600,7 @@ iwm_init_channel_map(struct iwm_softc *sc, const uint16_t * const nvm_ch_flags) int is_5ghz; int flags, hw_value; - for (ch_idx = 0; ch_idx < nitems(iwm_nvm_channels); ch_idx++) { + for (ch_idx = 0; ch_idx < nchan; ch_idx++) { ch_flags = le16_to_cpup(nvm_ch_flags + ch_idx); if (ch_idx >= IWM_NUM_2GHZ_CHANNELS && @@ -2443,14 +2609,14 @@ iwm_init_channel_map(struct iwm_softc *sc, const uint16_t * const nvm_ch_flags) if (!(ch_flags & IWM_NVM_CHANNEL_VALID)) { DPRINTF(("Ch. %d Flags %x [%sGHz] - No traffic\n", - iwm_nvm_channels[ch_idx], + nvm_channels[ch_idx], ch_flags, (ch_idx >= IWM_NUM_2GHZ_CHANNELS) ? "5.2" : "2.4")); continue; } - hw_value = iwm_nvm_channels[ch_idx]; + hw_value = nvm_channels[ch_idx]; channel = &ic->ic_channels[hw_value]; is_5ghz = ch_idx >= IWM_NUM_2GHZ_CHANNELS; @@ -2490,9 +2656,9 @@ iwm_setup_ht_rates(struct iwm_softc *sc) if (sc->sc_nvm.sku_cap_mimo_disable) return; - if (IWM_FW_VALID_RX_ANT(sc) > 1) + if (iwm_fw_valid_rx_ant(sc) > 1) ic->ic_sup_mcs[1] = 0xff; /* MCS 8-15 */ - if (IWM_FW_VALID_RX_ANT(sc) > 2) + if (iwm_fw_valid_rx_ant(sc) > 2) ic->ic_sup_mcs[2] = 0xff; /* MCS 16-23 */ #endif } @@ -2503,7 +2669,7 @@ void iwm_mvm_sta_rx_agg(struct iwm_softc *sc, struct ieee80211_node *ni, uint8_t tid, uint16_t ssn, int start) { - struct iwm_mvm_add_sta_cmd_v6 cmd; + struct iwm_mvm_add_sta_cmd_v7 cmd; struct iwm_node *in = (void *)ni; int ret, s; uint32_t status; @@ -2637,11 +2803,13 @@ iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni, int iwm_parse_nvm_data(struct iwm_softc *sc, const uint16_t *nvm_hw, const uint16_t *nvm_sw, - const uint16_t *nvm_calib, uint8_t tx_chains, uint8_t rx_chains) + const uint16_t *nvm_calib, const uint16_t *mac_override, + const uint16_t *phy_sku, const uint16_t *regulatory) { struct iwm_nvm_data *data = &sc->sc_nvm; uint8_t hw_addr[ETHER_ADDR_LEN]; - uint16_t radio_cfg, sku; + uint16_t radio_cfg; + uint32_t sku; data->nvm_version = le16_to_cpup(nvm_sw + IWM_NVM_VERSION); @@ -2668,9 +2836,6 @@ iwm_parse_nvm_data(struct iwm_softc *sc, data->n_hw_addrs = le16_to_cpup(nvm_sw + IWM_N_HW_ADDRS); - data->xtal_calib[0] = *(nvm_calib + IWM_XTAL_CALIB); - data->xtal_calib[1] = *(nvm_calib + IWM_XTAL_CALIB + 1); - /* The byte order is little endian 16 bit, meaning 214365 */ memcpy(hw_addr, nvm_hw + IWM_HW_ADDR, ETHER_ADDR_LEN); data->hw_addr[0] = hw_addr[1]; @@ -2680,7 +2845,9 @@ iwm_parse_nvm_data(struct iwm_softc *sc, data->hw_addr[4] = hw_addr[5]; data->hw_addr[5] = hw_addr[4]; - iwm_init_channel_map(sc, &nvm_sw[IWM_NVM_CHANNELS]); + iwm_init_channel_map(sc, &nvm_sw[IWM_NVM_CHANNELS], + iwm_nvm_channels, nitems(iwm_nvm_channels)); + data->calib_version = 255; /* TODO: this value will prevent some checks from failing, we need to check if this @@ -2690,32 +2857,27 @@ iwm_parse_nvm_data(struct iwm_softc *sc, return 0; } -/* - * END NVM PARSE - */ - -struct iwm_nvm_section { - uint16_t length; - const uint8_t *data; -}; - int iwm_parse_nvm_sections(struct iwm_softc *sc, struct iwm_nvm_section *sections) { - const uint16_t *hw, *sw, *calib; + const uint16_t *hw, *sw, *calib, *mac_override = NULL, *phy_sku = NULL; + const uint16_t *regulatory = NULL; /* Checking for required sections */ if (!sections[IWM_NVM_SECTION_TYPE_SW].data || !sections[IWM_NVM_SECTION_TYPE_HW].data) { - DPRINTF(("%s: Can't parse empty NVM sections\n", DEVNAME(sc))); + DPRINTF(("%s: Can't parse empty OTP/NVM sections\n", + DEVNAME(sc))); return ENOENT; } - hw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_HW].data; + hw = (const uint16_t *) sections[IWM_NVM_SECTION_TYPE_HW].data; sw = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_SW].data; - calib = (const uint16_t *)sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; - return iwm_parse_nvm_data(sc, hw, sw, calib, - IWM_FW_VALID_TX_ANT(sc), IWM_FW_VALID_RX_ANT(sc)); + calib = (const uint16_t *) + sections[IWM_NVM_SECTION_TYPE_CALIBRATION].data; + + return iwm_parse_nvm_data(sc, hw, sw, calib, mac_override, + phy_sku, regulatory); } int @@ -2724,27 +2886,33 @@ iwm_nvm_init(struct iwm_softc *sc) struct iwm_nvm_section nvm_sections[IWM_NVM_NUM_OF_SECTIONS]; int i, section, error; uint16_t len; - uint8_t *nvm_buffer, *temp; + uint8_t *buf; + const size_t bufsz = IWM_MAX_NVM_SECTION_SIZE; - /* Read From FW NVM */ - DPRINTF(("Read NVM\n")); + memset(nvm_sections, 0, sizeof(nvm_sections)); + + buf = malloc(bufsz, M_DEVBUF, M_WAIT); + if (buf == NULL) + return ENOMEM; - /* TODO: find correct NVM max size for a section */ - nvm_buffer = malloc(IWM_OTP_LOW_IMAGE_SIZE, M_DEVBUF, M_WAIT); for (i = 0; i < nitems(nvm_to_read); i++) { section = nvm_to_read[i]; KASSERT(section <= nitems(nvm_sections)); - error = iwm_nvm_read_section(sc, section, nvm_buffer, &len); - if (error) + error = iwm_nvm_read_section(sc, section, buf, &len, bufsz); + if (error) { + error = 0; + continue; + } + nvm_sections[section].data = malloc(len, M_DEVBUF, M_WAIT); + if (nvm_sections[section].data == NULL) { + error = ENOMEM; break; - - temp = malloc(len, M_DEVBUF, M_WAIT); - memcpy(temp, nvm_buffer, len); - nvm_sections[section].data = temp; + } + memcpy(nvm_sections[section].data, buf, len); nvm_sections[section].length = len; } - free(nvm_buffer, M_DEVBUF, IWM_OTP_LOW_IMAGE_SIZE); + free(buf, M_DEVBUF, bufsz); if (error) return error; @@ -2757,22 +2925,52 @@ iwm_nvm_init(struct iwm_softc *sc) */ int -iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, +iwm_firmware_load_sect(struct iwm_softc *sc, uint32_t dst_addr, const uint8_t *section, uint32_t byte_cnt) { + int error = EINVAL; + uint32_t chunk_sz, offset; + + chunk_sz = MIN(IWM_FH_MEM_TB_MAX_LENGTH, byte_cnt); + + for (offset = 0; offset < byte_cnt; offset += chunk_sz) { + uint32_t addr, len; + const uint8_t *data; + + addr = dst_addr + offset; + len = MIN(chunk_sz, byte_cnt - offset); + data = section + offset; + + error = iwm_firmware_load_chunk(sc, addr, data, len); + if (error) + break; + } + + return error; +} + +int +iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, + const uint8_t *chunk, uint32_t byte_cnt) +{ struct iwm_dma_info *dma = &sc->fw_dma; int error; - /* Copy firmware section into pre-allocated DMA-safe memory. */ - memcpy(dma->vaddr, section, byte_cnt); + /* Copy firmware chunk into pre-allocated DMA-safe memory. */ + memcpy(dma->vaddr, chunk, byte_cnt); bus_dmamap_sync(sc->sc_dmat, dma->map, 0, byte_cnt, BUS_DMASYNC_PREWRITE); - if (!iwm_nic_lock(sc)) - return EBUSY; + if (dst_addr >= IWM_FW_MEM_EXTENDED_START && + dst_addr <= IWM_FW_MEM_EXTENDED_END) + iwm_set_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); sc->sc_fw_chunk_done = 0; + if (!iwm_nic_lock(sc)) + return EBUSY; + IWM_WRITE(sc, IWM_FH_TCSR_CHNL_TX_CONFIG_REG(IWM_FH_SRVC_CHNL), IWM_FH_TCSR_TX_CONFIG_REG_VAL_DMA_CHNL_PAUSE); IWM_WRITE(sc, IWM_FH_SRVC_CHNL_SRAM_ADDR_REG(IWM_FH_SRVC_CHNL), @@ -2798,14 +2996,25 @@ iwm_firmware_load_chunk(struct iwm_softc *sc, uint32_t dst_addr, if ((error = tsleep(&sc->sc_fw, 0, "iwmfw", hz)) != 0) break; + if (!sc->sc_fw_chunk_done) + DPRINTF(("%s: fw chunk addr 0x%x len %d failed to load\n", + DEVNAME(sc), dst_addr, byte_cnt)); + + if (dst_addr >= IWM_FW_MEM_EXTENDED_START && + dst_addr <= IWM_FW_MEM_EXTENDED_END && iwm_nic_lock(sc)) { + iwm_clear_bits_prph(sc, IWM_LMPM_CHICK, + IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE); + iwm_nic_unlock(sc); + } + return error; } int -iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +iwm_load_firmware_7000(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) { struct iwm_fw_sects *fws; - int error, i, w; + int error, i; void *data; uint32_t dlen; uint32_t offset; @@ -2817,21 +3026,47 @@ iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) data = fws->fw_sect[i].fws_data; dlen = fws->fw_sect[i].fws_len; offset = fws->fw_sect[i].fws_devoff; - DPRINTF(("LOAD FIRMWARE type %d offset %u len %d\n", + DPRINTFN(4, ("LOAD FIRMWARE type %d offset %u len %d\n", ucode_type, offset, dlen)); - error = iwm_firmware_load_chunk(sc, offset, data, dlen); + if (dlen > sc->sc_fwdmasegsz) { + DPRINTF(("chunk %d too large (%d bytes)\n", i, dlen)); + error = EFBIG; + } else + error = iwm_firmware_load_sect(sc, offset, data, dlen); if (error) { - DPRINTF(("iwm_firmware_load_chunk() chunk %u of %u returned error %02d\n", i, fws->fw_count, error)); + printf("%s: could not load firmware chunk %u of %u\n", + DEVNAME(sc), i, fws->fw_count); return error; } } - /* wait for the firmware to load */ IWM_WRITE(sc, IWM_CSR_RESET, 0); + return 0; +} + +int +iwm_load_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +{ + int error, w; + + error = iwm_load_firmware_7000(sc, ucode_type); + if (error) + return error; + + /* wait for the firmware to load */ for (w = 0; !sc->sc_uc.uc_intr && w < 10; w++) { error = tsleep(&sc->sc_uc, 0, "iwmuc", hz/10); } + if (error || !sc->sc_uc.uc_ok) { + printf("%s: could not load firmware\n", DEVNAME(sc)); + } + + /* + * Give the firmware some time to initialize. + * Accessing it too early causes errors. + */ + tsleep(&w, PCATCH, "iwmfwinit", hz); return error; } @@ -2948,23 +3183,31 @@ iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) IEEE80211_ADDR_COPY(sc->sc_ic.ic_myaddr, sc->sc_nvm.hw_addr); - sc->sc_scan_cmd_len = sizeof(struct iwm_scan_cmd) - + sc->sc_capa_max_probe_len - + IWM_MAX_NUM_SCAN_CHANNELS - * sizeof(struct iwm_scan_channel); - sc->sc_scan_cmd = malloc(sc->sc_scan_cmd_len, M_DEVBUF, M_WAIT); - return 0; } + if ((error = iwm_send_bt_init_conf(sc)) != 0) { + DPRINTF(("%s: failed to send bt coex configuration: %d\n", + DEVNAME(sc), error)); + return error; + } + + /* Init Smart FIFO. */ + error = iwm_mvm_sf_config(sc, IWM_SF_INIT_OFF); + if (error != 0) + return error; + /* Send TX valid antennas before triggering calibrations */ - if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) + if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) { + DPRINTF(("%s: failed to send antennas before calibration: %d\n", + DEVNAME(sc), error)); return error; + } /* - * Send phy configurations command to init uCode - * to start the 16.0 uCode init image internal calibrations. - */ + * Send phy configurations command to init uCode + * to start the 16.0 uCode init image internal calibrations. + */ if ((error = iwm_send_phy_cfg_cmd(sc)) != 0 ) { DPRINTF(("%s: failed to run internal calibration: %d\n", DEVNAME(sc), error)); @@ -2980,6 +3223,9 @@ iwm_run_init_mvm_ucode(struct iwm_softc *sc, int justnvm) 0, "iwminit", 2*hz)) != 0) break; + DPRINTF(("%s: init %scomplete\n", DEVNAME(sc), + sc->sc_init_complete ? "" : "not ")); + return error; } @@ -3192,10 +3438,8 @@ iwm_mvm_rx_rx_mpdu(struct iwm_softc *sc, if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0) return; - if (sc->sc_scanband == IEEE80211_CHAN_5GHZ) { - if (le32toh(phy_info->channel) < nitems(ic->ic_channels)) - c = &ic->ic_channels[le32toh(phy_info->channel)]; - } + if (le32toh(phy_info->channel) < nitems(ic->ic_channels)) + c = &ic->ic_channels[le32toh(phy_info->channel)]; memset(&rxi, 0, sizeof(rxi)); rxi.rxi_rssi = rssi; @@ -3381,15 +3625,15 @@ iwm_mvm_binding_cmd(struct iwm_softc *sc, struct iwm_node *in, uint32_t action) } int -iwm_mvm_binding_update(struct iwm_softc *sc, struct iwm_node *in, int add) +iwm_mvm_binding_update(struct iwm_softc *sc, struct iwm_node *in) { - return iwm_mvm_binding_cmd(sc, in, IWM_FW_CTXT_ACTION_ADD); + return iwm_mvm_binding_cmd(sc, in, IWM_FW_CTXT_ACTION_MODIFY); } int iwm_mvm_binding_add_vif(struct iwm_softc *sc, struct iwm_node *in) { - return iwm_mvm_binding_update(sc, in, IWM_FW_CTXT_ACTION_ADD); + return iwm_mvm_binding_cmd(sc, in, IWM_FW_CTXT_ACTION_ADD); } /* @@ -3429,13 +3673,13 @@ iwm_mvm_phy_ctxt_cmd_data(struct iwm_softc *sc, idle_cnt = chains_static; active_cnt = chains_dynamic; - cmd->rxchain_info = htole32(IWM_FW_VALID_RX_ANT(sc) << + cmd->rxchain_info = htole32(iwm_fw_valid_rx_ant(sc) << IWM_PHY_RX_CHAIN_VALID_POS); cmd->rxchain_info |= htole32(idle_cnt << IWM_PHY_RX_CHAIN_CNT_POS); cmd->rxchain_info |= htole32(active_cnt << IWM_PHY_RX_CHAIN_MIMO_CNT_POS); - cmd->txchain_info = htole32(IWM_FW_VALID_TX_ANT(sc)); + cmd->txchain_info = htole32(iwm_fw_valid_tx_ant(sc)); } /* @@ -3512,7 +3756,7 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) { struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; struct iwm_tfd *desc; - struct iwm_tx_data *data; + struct iwm_tx_data *txdata; struct iwm_device_cmd *cmd; struct mbuf *m; bus_addr_t paddr; @@ -3520,6 +3764,9 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) int error = 0, i, paylen, off, s; int code; int async, wantresp; + int group_id; + size_t hdrlen, datasz; + uint8_t *data; code = hcmd->id; async = hcmd->flags & IWM_CMD_ASYNC; @@ -3548,11 +3795,20 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) } desc = &ring->desc[ring->cur]; - data = &ring->data[ring->cur]; + txdata = &ring->data[ring->cur]; - if (paylen > sizeof(cmd->data)) { + group_id = iwm_cmd_groupid(code); + if (group_id != 0) { + hdrlen = sizeof(cmd->hdr_wide); + datasz = sizeof(cmd->data_wide); + } else { + hdrlen = sizeof(cmd->hdr); + datasz = sizeof(cmd->data); + } + + if (paylen > datasz) { /* Command is too large to fit in pre-allocated space. */ - size_t totlen = sizeof(cmd->hdr) + paylen; + size_t totlen = hdrlen + paylen; if (paylen > IWM_MAX_CMD_PAYLOAD_SIZE) { printf("%s: firmware command too long (%zd bytes)\n", DEVNAME(sc), totlen); @@ -3567,7 +3823,7 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) goto out; } cmd = mtod(m, struct iwm_device_cmd *); - error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, + error = bus_dmamap_load(sc->sc_dmat, txdata->map, cmd, totlen, NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); if (error != 0) { printf("%s: could not load fw cmd mbuf (%zd bytes)\n", @@ -3575,22 +3831,33 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) m_freem(m); goto out; } - data->m = m; /* mbuf will be freed in iwm_cmd_done() */ - paddr = data->map->dm_segs[0].ds_addr; + txdata->m = m; /* mbuf will be freed in iwm_cmd_done() */ + paddr = txdata->map->dm_segs[0].ds_addr; } else { cmd = &ring->cmd[ring->cur]; - paddr = data->cmd_paddr; + paddr = txdata->cmd_paddr; } - cmd->hdr.code = code; - cmd->hdr.flags = 0; - cmd->hdr.qid = ring->qid; - cmd->hdr.idx = ring->cur; + if (group_id != 0) { + cmd->hdr_wide.opcode = iwm_cmd_opcode(code); + cmd->hdr_wide.group_id = group_id; + cmd->hdr_wide.qid = ring->qid; + cmd->hdr_wide.idx = ring->cur; + cmd->hdr_wide.length = htole16(paylen); + cmd->hdr_wide.version = iwm_cmd_version(code); + data = cmd->data_wide; + } else { + cmd->hdr.code = code; + cmd->hdr.flags = 0; + cmd->hdr.qid = ring->qid; + cmd->hdr.idx = ring->cur; + data = cmd->data; + } for (i = 0, off = 0; i < nitems(hcmd->data); i++) { if (hcmd->len[i] == 0) continue; - memcpy(cmd->data + off, hcmd->data[i], hcmd->len[i]); + memcpy(data + off, hcmd->data[i], hcmd->len[i]); off += hcmd->len[i]; } KASSERT(off == paylen); @@ -3599,20 +3866,20 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) addr_lo = htole32((uint32_t)paddr); memcpy(&desc->tbs[0].lo, &addr_lo, sizeof(uint32_t)); desc->tbs[0].hi_n_len = htole16(iwm_get_dma_hi_addr(paddr) - | ((sizeof(cmd->hdr) + paylen) << 4)); + | ((hdrlen + paylen) << 4)); desc->num_tbs = 1; DPRINTFN(8, ("iwm_send_cmd 0x%x size=%lu %s\n", - code, hcmd->len[0] + hcmd->len[1] + sizeof(cmd->hdr), + code, hcmd->len[0] + hcmd->len[1] + hdrlen, async ? " (async)" : "")); - if (paylen > sizeof(cmd->data)) { - bus_dmamap_sync(sc->sc_dmat, data->map, 0, - sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); + if (paylen > datasz) { + bus_dmamap_sync(sc->sc_dmat, txdata->map, 0, + hdrlen + paylen, BUS_DMASYNC_PREWRITE); } else { bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, - sizeof(cmd->hdr) + hcmd->len[0], BUS_DMASYNC_PREWRITE); + hdrlen + paylen, BUS_DMASYNC_PREWRITE); } bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, @@ -4314,7 +4581,7 @@ iwm_mvm_enable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) } int -iwm_mvm_disable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) +iwm_mvm_disable_beacon_filter(struct iwm_softc *sc) { struct iwm_beacon_filter_cmd cmd; int ret; @@ -4330,63 +4597,19 @@ iwm_mvm_disable_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) return ret; } -#if 0 -int -iwm_mvm_update_beacon_filter(struct iwm_softc *sc, struct iwm_node *in) -{ - if (!sc->sc_bf.bf_enabled) - return 0; - - return iwm_mvm_enable_beacon_filter(sc, in); -} -#endif - -void -iwm_mvm_add_sta_cmd_v6_to_v5(struct iwm_mvm_add_sta_cmd_v6 *cmd_v6, - struct iwm_mvm_add_sta_cmd_v5 *cmd_v5) -{ - memset(cmd_v5, 0, sizeof(*cmd_v5)); - - cmd_v5->add_modify = cmd_v6->add_modify; - cmd_v5->tid_disable_tx = cmd_v6->tid_disable_tx; - cmd_v5->mac_id_n_color = cmd_v6->mac_id_n_color; - memcpy(cmd_v5->addr, cmd_v6->addr, ETHER_ADDR_LEN); - cmd_v5->sta_id = cmd_v6->sta_id; - cmd_v5->modify_mask = cmd_v6->modify_mask; - cmd_v5->station_flags = cmd_v6->station_flags; - cmd_v5->station_flags_msk = cmd_v6->station_flags_msk; - cmd_v5->add_immediate_ba_tid = cmd_v6->add_immediate_ba_tid; - cmd_v5->remove_immediate_ba_tid = cmd_v6->remove_immediate_ba_tid; - cmd_v5->add_immediate_ba_ssn = cmd_v6->add_immediate_ba_ssn; - cmd_v5->sleep_tx_count = cmd_v6->sleep_tx_count; - cmd_v5->sleep_state_flags = cmd_v6->sleep_state_flags; - cmd_v5->assoc_id = cmd_v6->assoc_id; - cmd_v5->beamform_flags = cmd_v6->beamform_flags; - cmd_v5->tfd_queue_msk = cmd_v6->tfd_queue_msk; -} - int iwm_mvm_send_add_sta_cmd_status(struct iwm_softc *sc, - struct iwm_mvm_add_sta_cmd_v6 *cmd, int *status) + struct iwm_mvm_add_sta_cmd_v7 *cmd, int *status) { - struct iwm_mvm_add_sta_cmd_v5 cmd_v5; - - if (sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_STA_KEY_CMD) { - return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, - sizeof(*cmd), cmd, status); - } - - iwm_mvm_add_sta_cmd_v6_to_v5(cmd, &cmd_v5); - - return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(cmd_v5), - &cmd_v5, status); + return iwm_mvm_send_cmd_pdu_status(sc, IWM_ADD_STA, sizeof(*cmd), + cmd, status); } /* send station add/update command to firmware */ int iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) { - struct iwm_mvm_add_sta_cmd_v6 add_sta_cmd; + struct iwm_mvm_add_sta_cmd_v7 add_sta_cmd; int ret; uint32_t status; struct ieee80211com *ic = &sc->sc_ic; @@ -4397,12 +4620,19 @@ iwm_mvm_sta_send_to_fw(struct iwm_softc *sc, struct iwm_node *in, int update) add_sta_cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(in->in_id, in->in_color)); if (!update) { - add_sta_cmd.tfd_queue_msk = htole32(0xf); + int ac; + for (ac = 0; ac < EDCA_NUM_AC; ac++) { + add_sta_cmd.tfd_queue_msk |= + htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); + } IEEE80211_ADDR_COPY(&add_sta_cmd.addr, in->in_ni.ni_bssid); } add_sta_cmd.add_modify = update ? 1 : 0; add_sta_cmd.station_flags_msk |= htole32(IWM_STA_FLG_FAT_EN_MSK | IWM_STA_FLG_MIMO_EN_MSK); + add_sta_cmd.tid_disable_tx = htole16(0xffff); + if (update) + add_sta_cmd.modify_mask |= (IWM_STA_MODIFY_TID_DISABLE_TX); if (in->in_ni.ni_flags & IEEE80211_NODE_HT) { add_sta_cmd.station_flags_msk @@ -4466,7 +4696,7 @@ int iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, const uint8_t *addr, uint16_t mac_id, uint16_t color) { - struct iwm_mvm_add_sta_cmd_v6 cmd; + struct iwm_mvm_add_sta_cmd_v7 cmd; int ret; uint32_t status; @@ -4475,6 +4705,7 @@ iwm_mvm_add_int_sta_common(struct iwm_softc *sc, struct iwm_int_sta *sta, cmd.mac_id_n_color = htole32(IWM_FW_CMD_ID_AND_COLOR(mac_id, color)); cmd.tfd_queue_msk = htole32(sta->tfd_queue_msk); + cmd.tid_disable_tx = htole16(0xffff); if (addr) memcpy(cmd.addr, addr, ETHER_ADDR_LEN); @@ -4501,8 +4732,12 @@ iwm_mvm_add_aux_sta(struct iwm_softc *sc) { int ret; - sc->sc_aux_sta.sta_id = 3; - sc->sc_aux_sta.tfd_queue_msk = 0; + sc->sc_aux_sta.sta_id = IWM_AUX_STA_ID; + sc->sc_aux_sta.tfd_queue_msk = (1 << IWM_MVM_AUX_QUEUE); + + ret = iwm_enable_txq(sc, 0, IWM_MVM_AUX_QUEUE, IWM_MVM_TX_FIFO_MCAST); + if (ret) + return ret; ret = iwm_mvm_add_int_sta_common(sc, &sc->sc_aux_sta, NULL, IWM_MAC_INDEX_AUX, 0); @@ -4524,7 +4759,7 @@ iwm_mvm_scan_rx_chain(struct iwm_softc *sc) uint16_t rx_chain; uint8_t rx_ant; - rx_ant = IWM_FW_VALID_RX_ANT(sc); + rx_ant = iwm_fw_valid_rx_ant(sc); rx_chain = rx_ant << IWM_PHY_RX_CHAIN_VALID_POS; rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_MIMO_SEL_POS; rx_chain |= rx_ant << IWM_PHY_RX_CHAIN_FORCE_SEL_POS; @@ -4553,15 +4788,6 @@ iwm_mvm_scan_suspend_time(struct iwm_softc *sc, int is_assoc) } uint32_t -iwm_mvm_scan_rxon_flags(struct iwm_softc *sc, int flags) -{ - if (flags & IEEE80211_CHAN_2GHZ) - return htole32(IWM_PHY_BAND_24); - else - return htole32(IWM_PHY_BAND_5); -} - -uint32_t iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck) { uint32_t tx_ant; @@ -4570,7 +4796,7 @@ iwm_mvm_scan_rate_n_flags(struct iwm_softc *sc, int flags, int no_cck) for (i = 0, ind = sc->sc_scan_last_antenna; i < IWM_RATE_MCS_ANT_NUM; i++) { ind = (ind + 1) % IWM_RATE_MCS_ANT_NUM; - if (IWM_FW_VALID_TX_ANT(sc) & (1 << ind)) { + if (iwm_fw_valid_tx_ant(sc) & (1 << ind)) { sc->sc_scan_last_antenna = ind; break; } @@ -4608,192 +4834,236 @@ iwm_mvm_get_passive_dwell(struct iwm_softc *sc, int flags) return (flags & IEEE80211_CHAN_2GHZ) ? 100 + 20 : 100 + 10; } -int -iwm_mvm_scan_fill_channels(struct iwm_softc *sc, struct iwm_scan_cmd *cmd, - int flags, int n_ssids, int basic_ssid) +uint8_t +iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc, + struct iwm_scan_channel_cfg_lmac *chan, int n_ssids) { struct ieee80211com *ic = &sc->sc_ic; - uint16_t passive_dwell = iwm_mvm_get_passive_dwell(sc, flags); - uint16_t active_dwell = iwm_mvm_get_active_dwell(sc, flags, n_ssids); - struct iwm_scan_channel *chan = (struct iwm_scan_channel *) - (cmd->data + le16toh(cmd->tx_cmd.len)); - int type = (1 << n_ssids) - 1; struct ieee80211_channel *c; - int nchan; - - if (!basic_ssid) - type |= (1 << n_ssids); + uint8_t nchan; for (nchan = 0, c = &ic->ic_channels[1]; - c <= &ic->ic_channels[IEEE80211_CHAN_MAX]; + c <= &ic->ic_channels[IEEE80211_CHAN_MAX] && + nchan < sc->sc_capa_n_scan_channels; c++) { - if ((c->ic_flags & flags) != flags) + if (c->ic_flags == 0) continue; - chan->channel = htole16(ieee80211_mhz2ieee(c->ic_freq, flags)); - chan->type = htole32(type); - if (c->ic_flags & IEEE80211_CHAN_PASSIVE) - chan->type &= htole32(~IWM_SCAN_CHANNEL_TYPE_ACTIVE); - chan->active_dwell = htole16(active_dwell); - chan->passive_dwell = htole16(passive_dwell); - chan->iteration_count = htole16(1); + chan->channel_num = htole16(ieee80211_mhz2ieee(c->ic_freq, 0)); + chan->iter_count = htole16(1); + chan->iter_interval = 0; + chan->flags = htole32(IWM_UNIFIED_SCAN_CHANNEL_PARTIAL); +#if 0 /* makes scanning while associated less useful */ + if (n_ssids != 0) + chan->flags |= htole32(1 << 1); /* select SSID 0 */ +#endif chan++; nchan++; } - if (nchan == 0) - DPRINTF(("%s: NO CHANNEL!\n", DEVNAME(sc))); + return nchan; } -/* - * Fill in probe request with the following parameters: - * TA is our vif HW address, which mac80211 ensures we have. - * Packet is broadcasted, so this is both SA and DA. - * The probe request IE is made out of two: first comes the most prioritized - * SSID if a directed scan is requested. Second comes whatever extra - * information was given to us as the scan request IE. - */ -uint16_t -iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct ieee80211_frame *frame, - const uint8_t *ta, int n_ssids, const uint8_t *ssid, int ssid_len, - const uint8_t *ie, int ie_len, int left) +int +iwm_mvm_fill_probe_req(struct iwm_softc *sc, struct iwm_scan_probe_req *preq) { - int len = 0; - uint8_t *pos = NULL; - - /* Make sure there is enough space for the probe request, - * two mandatory IEs and the data */ - left -= sizeof(*frame); - if (left < 0) - return 0; - - frame->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | - IEEE80211_FC0_SUBTYPE_PROBE_REQ; - frame->i_fc[1] = IEEE80211_FC1_DIR_NODS; - IEEE80211_ADDR_COPY(frame->i_addr1, etherbroadcastaddr); - memcpy(frame->i_addr2, ta, ETHER_ADDR_LEN); - IEEE80211_ADDR_COPY(frame->i_addr3, etherbroadcastaddr); - - len += sizeof(*frame); - CTASSERT(sizeof(*frame) == 24); - - /* for passive scans, no need to fill anything */ - if (n_ssids == 0) - return (uint16_t)len; - - /* points to the payload of the request */ - pos = (uint8_t *)frame + sizeof(*frame); - - /* fill in our SSID IE */ - left -= ssid_len + 2; - if (left < 0) - return 0; - *pos++ = IEEE80211_ELEMID_SSID; - *pos++ = ssid_len; - if (ssid && ssid_len) { /* ssid_len may be == 0 even if ssid is valid */ - memcpy(pos, ssid, ssid_len); - pos += ssid_len; - } + struct ieee80211com *ic = &sc->sc_ic; + struct ifnet *ifp = IC2IFP(ic); + struct ieee80211_frame *wh = (struct ieee80211_frame *)preq->buf; + struct ieee80211_rateset *rs; + size_t remain = sizeof(preq->buf); + uint8_t *frm, *pos; - len += ssid_len + 2; + memset(preq, 0, sizeof(*preq)); - if (left < ie_len) - return len; + /* Ensure enough space for header and SSID IE. */ + if (remain < sizeof(*wh) + 2 + ic->ic_des_esslen) + return ENOBUFS; - if (ie && ie_len) { - memcpy(pos, ie, ie_len); - len += ie_len; + /* + * Build a probe request frame. Most of the following code is a + * copy & paste of what is done in net80211. + */ + wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT | + IEEE80211_FC0_SUBTYPE_PROBE_REQ; + wh->i_fc[1] = IEEE80211_FC1_DIR_NODS; + IEEE80211_ADDR_COPY(ic->ic_myaddr, LLADDR(ifp->if_sadl)); + IEEE80211_ADDR_COPY(wh->i_addr1, etherbroadcastaddr); + IEEE80211_ADDR_COPY(wh->i_addr2, ic->ic_myaddr); + IEEE80211_ADDR_COPY(wh->i_addr3, etherbroadcastaddr); + *(uint16_t *)&wh->i_dur[0] = 0; /* filled by HW */ + *(uint16_t *)&wh->i_seq[0] = 0; /* filled by HW */ + + frm = (uint8_t *)(wh + 1); + frm = ieee80211_add_ssid(frm, ic->ic_des_essid, ic->ic_des_esslen); + + /* Tell the firmware where the MAC header is. */ + preq->mac_header.offset = 0; + preq->mac_header.len = htole16(frm - (uint8_t *)wh); + remain -= frm - (uint8_t *)wh; + + /* Fill in 2GHz IEs and tell firmware where they are. */ + rs = &ic->ic_sup_rates[IEEE80211_MODE_11G]; + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + if (remain < 4 + rs->rs_nrates) + return ENOBUFS; + } else if (remain < 2 + rs->rs_nrates) + return ENOBUFS; + preq->band_data[0].offset = htole16(frm - (uint8_t *)wh); + pos = frm; + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + preq->band_data[0].len = htole16(frm - pos); + remain -= frm - pos; + + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) { + if (remain < 3) + return ENOBUFS; + *frm++ = IEEE80211_ELEMID_DSPARMS; + *frm++ = 1; + *frm++ = 0; + remain -= 3; + } + + if (sc->sc_nvm.sku_cap_band_52GHz_enable) { + /* Fill in 5GHz IEs. */ + rs = &ic->ic_sup_rates[IEEE80211_MODE_11A]; + if (rs->rs_nrates > IEEE80211_RATE_SIZE) { + if (remain < 4 + rs->rs_nrates) + return ENOBUFS; + } else if (remain < 2 + rs->rs_nrates) + return ENOBUFS; + preq->band_data[1].offset = htole16(frm - (uint8_t *)wh); + pos = frm; + frm = ieee80211_add_rates(frm, rs); + if (rs->rs_nrates > IEEE80211_RATE_SIZE) + frm = ieee80211_add_xrates(frm, rs); + preq->band_data[1].len = htole16(frm - pos); + remain -= frm - pos; + } + + /* Send 11n IEs on both 2GHz and 5GHz bands. */ + preq->common_data.offset = htole16(frm - (uint8_t *)wh); + pos = frm; + if (ic->ic_flags & IEEE80211_F_HTON) { + if (remain < 28) + return ENOBUFS; + frm = ieee80211_add_htcaps(frm, ic); + /* XXX add WME info? */ } + preq->common_data.len = htole16(frm - pos); - return (uint16_t)len; + return 0; } int -iwm_mvm_scan_request(struct iwm_softc *sc, int flags, - int n_ssids, uint8_t *ssid, int ssid_len) +iwm_mvm_lmac_scan(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct iwm_host_cmd hcmd = { - .id = IWM_SCAN_REQUEST_CMD, + .id = IWM_SCAN_OFFLOAD_REQUEST_CMD, .len = { 0, }, - .data = { sc->sc_scan_cmd, }, + .data = { NULL, }, .flags = IWM_CMD_SYNC, - .dataflags = { IWM_HCMD_DFL_NOCOPY, }, }; - struct iwm_scan_cmd *cmd = sc->sc_scan_cmd; - int is_assoc = 0; + struct iwm_scan_req_lmac *req; + size_t req_len; int ret; - uint32_t status; - int basic_ssid = !(sc->sc_capaflags & IWM_UCODE_TLV_FLAGS_NO_BASIC_SSID); - //lockdep_assert_held(&mvm->mutex); + req_len = sizeof(struct iwm_scan_req_lmac) + + (sizeof(struct iwm_scan_channel_cfg_lmac) * + sc->sc_capa_n_scan_channels) + sizeof(struct iwm_scan_probe_req); + if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE) + return ENOMEM; + req = malloc(req_len, M_DEVBUF, M_WAIT | M_CANFAIL | M_ZERO); + if (req == NULL) + return ENOMEM; - sc->sc_scanband = flags & (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_5GHZ); + hcmd.len[0] = (uint16_t)req_len; + hcmd.data[0] = (void *)req; DPRINTF(("Handling ieee80211 scan request\n")); - memset(cmd, 0, sc->sc_scan_cmd_len); - cmd->quiet_time = htole16(IWM_ACTIVE_QUIET_TIME); - cmd->quiet_plcp_th = htole16(IWM_PLCP_QUIET_THRESH); - cmd->rxchain_sel_flags = iwm_mvm_scan_rx_chain(sc); - cmd->max_out_time = iwm_mvm_scan_max_out_time(sc, 0, is_assoc); - cmd->suspend_time = iwm_mvm_scan_suspend_time(sc, is_assoc); - cmd->rxon_flags = iwm_mvm_scan_rxon_flags(sc, flags); - cmd->filter_flags = htole32(IWM_MAC_FILTER_ACCEPT_GRP | - IWM_MAC_FILTER_IN_BEACON); - - cmd->type = htole32(IWM_SCAN_TYPE_FORCED); - cmd->repeats = htole32(1); + /* These timings correspond to iwlwifi's UNASSOC scan. */ + req->active_dwell = 10; + req->passive_dwell = 110; + req->fragmented_dwell = 44; + req->extended_dwell = 90; + req->max_out_time = 0; + req->suspend_time = 0; + + req->scan_prio = htole32(IWM_SCAN_PRIORITY_HIGH); + req->rx_chain_select = iwm_mvm_scan_rx_chain(sc); + req->iter_num = htole32(1); + req->delay = 0; + + req->scan_flags = htole32(IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL | + IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE | + IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL); + if (ic->ic_des_esslen == 0) + req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAG_PASSIVE); + else + req->scan_flags |= + htole32(IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION); + if (isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)) + req->scan_flags |= htole32(IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED); + + req->flags = htole32(IWM_PHY_BAND_24); + if (sc->sc_nvm.sku_cap_band_52GHz_enable) + req->flags |= htole32(IWM_PHY_BAND_5); + req->filter_flags = + htole32(IWM_MAC_FILTER_ACCEPT_GRP | IWM_MAC_FILTER_IN_BEACON); + + /* Tx flags 2 GHz. */ + req->tx_cmd[0].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | + IWM_TX_CMD_FLG_BT_DIS); + req->tx_cmd[0].rate_n_flags = + iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_2GHZ, 1/*XXX*/); + req->tx_cmd[0].sta_id = sc->sc_aux_sta.sta_id; - /* - * If the user asked for passive scan, don't change to active scan if - * you see any activity on the channel - remain passive. - */ - if (n_ssids > 0) { - cmd->passive2active = htole16(1); - cmd->scan_flags |= IWM_SCAN_FLAGS_PASSIVE2ACTIVE; -#if 0 - if (basic_ssid) { - ssid = req->ssids[0].ssid; - ssid_len = req->ssids[0].ssid_len; - } -#endif - } else { - cmd->passive2active = 0; - cmd->scan_flags &= ~IWM_SCAN_FLAGS_PASSIVE2ACTIVE; + /* Tx flags 5 GHz. */ + req->tx_cmd[1].tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | + IWM_TX_CMD_FLG_BT_DIS); + req->tx_cmd[1].rate_n_flags = + iwm_mvm_scan_rate_n_flags(sc, IEEE80211_CHAN_5GHZ, 1/*XXX*/); + req->tx_cmd[1].sta_id = sc->sc_aux_sta.sta_id; + + /* Check if we're doing an active directed scan. */ + if (ic->ic_des_esslen != 0) { + req->direct_scan[0].id = IEEE80211_ELEMID_SSID; + req->direct_scan[0].len = ic->ic_des_esslen; + memcpy(req->direct_scan[0].ssid, ic->ic_des_essid, + ic->ic_des_esslen); + } + + req->n_channels = iwm_mvm_lmac_scan_fill_channels(sc, + (struct iwm_scan_channel_cfg_lmac *)req->data, + ic->ic_des_esslen != 0); + + ret = iwm_mvm_fill_probe_req(sc, + (struct iwm_scan_probe_req *)(req->data + + (sizeof(struct iwm_scan_channel_cfg_lmac) * + sc->sc_capa_n_scan_channels))); + if (ret) { + free(req, M_DEVBUF, req_len); + return ret; } - cmd->tx_cmd.tx_flags = htole32(IWM_TX_CMD_FLG_SEQ_CTL | - IWM_TX_CMD_FLG_BT_DIS); - cmd->tx_cmd.sta_id = sc->sc_aux_sta.sta_id; - cmd->tx_cmd.life_time = htole32(IWM_TX_CMD_LIFE_TIME_INFINITE); - cmd->tx_cmd.rate_n_flags = iwm_mvm_scan_rate_n_flags(sc, flags, 1/*XXX*/); - - cmd->tx_cmd.len = htole16(iwm_mvm_fill_probe_req(sc, - (struct ieee80211_frame *)cmd->data, - ic->ic_myaddr, n_ssids, ssid, ssid_len, - NULL, 0, sc->sc_capa_max_probe_len)); - - cmd->channel_count - = iwm_mvm_scan_fill_channels(sc, cmd, flags, n_ssids, basic_ssid); - - cmd->len = htole16(sizeof(struct iwm_scan_cmd) + - le16toh(cmd->tx_cmd.len) + - (cmd->channel_count * sizeof(struct iwm_scan_channel))); - hcmd.len[0] = le16toh(cmd->len); - - status = IWM_SCAN_RESPONSE_OK; - ret = iwm_mvm_send_cmd_status(sc, &hcmd, &status); - if (!ret && status == IWM_SCAN_RESPONSE_OK) { + /* Specify the scan plan: We'll do one iteration. */ + req->schedule[0].iterations = 1; + req->schedule[0].full_scan_mul = 1; + + /* Disable EBS. */ + req->channel_opt[0].non_ebs_ratio = 1; + req->channel_opt[1].non_ebs_ratio = 1; + + ret = iwm_send_cmd(sc, &hcmd); + if (!ret) DPRINTF(("Scan request was sent successfully\n")); - } else { - /* - * If the scan failed, it usually means that the FW was unable - * to allocate the time events. Warn on it, but maybe we - * should try to send the command again with different params. - */ - ret = EIO; - } + free(req, M_DEVBUF, req_len); return ret; } @@ -5190,6 +5460,10 @@ iwm_auth(struct iwm_softc *sc) in->in_assoc = 0; + error = iwm_mvm_sf_config(sc, IWM_SF_FULL_ON); + if (error != 0) + return error; + error = iwm_allow_mcast(sc); if (error) return error; @@ -5207,7 +5481,12 @@ iwm_auth(struct iwm_softc *sc) } if ((error = iwm_mvm_add_sta(sc, in)) != 0) { - DPRINTF(("%s: failed to add MAC\n", DEVNAME(sc))); + DPRINTF(("%s: failed to add STA\n", DEVNAME(sc))); + return error; + } + + if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { + printf("%s: failed to update MAC\n", DEVNAME(sc)); return error; } @@ -5236,10 +5515,6 @@ iwm_assoc(struct iwm_softc *sc) } in->in_assoc = 1; - if ((error = iwm_mvm_mac_ctxt_changed(sc, in)) != 0) { - DPRINTF(("%s: failed to update MAC\n", DEVNAME(sc))); - return error; - } return 0; } @@ -5266,7 +5541,7 @@ iwm_release(struct iwm_softc *sc, struct iwm_node *in) * back to nothing anyway, we'll just do a complete device reset. * Up your's, device! */ - //iwm_mvm_flush_tx_path(sc, 0xf, 1); + /* iwm_mvm_flush_tx_path(sc, 0xf, 1); */ iwm_stop_device(sc); iwm_init_hw(sc); if (in) @@ -5370,6 +5645,10 @@ iwm_setrates(struct iwm_node *in) memset(lq, 0, sizeof(*lq)); lq->sta_id = IWM_STATION_ID; + /* For HT, always enable RTS/CTS to avoid excessive retries. */ + if (ni->ni_flags & IEEE80211_NODE_HT) + lq->flags |= IWM_LQ_FLAG_USE_RTS_MSK; + /* * Fill the LQ rate selection table with legacy and/or HT rates * in descending order, i.e. with the node's current TX rate first. @@ -5486,7 +5765,7 @@ iwm_newstate_task(void *psc) /* disable beacon filtering if we're hopping out of RUN */ if (ostate == IEEE80211_S_RUN && nstate != ostate) - iwm_mvm_disable_beacon_filter(sc, (void *)ic->ic_bss); + iwm_mvm_disable_beacon_filter(sc); /* Reset the device if moving out of AUTH, ASSOC, or RUN. */ if (ostate > IEEE80211_S_SCAN && nstate < ostate) { @@ -5509,7 +5788,8 @@ iwm_newstate_task(void *psc) nstate == IEEE80211_S_AUTH || nstate == IEEE80211_S_ASSOC) { DPRINTF(("Force transition to INIT; MGT=%d\n", arg)); - sc->sc_newstate(ic, IEEE80211_S_INIT, arg); + /* Always pass arg as -1 since we can't Tx right now. */ + sc->sc_newstate(ic, IEEE80211_S_INIT, -1); DPRINTF(("Going INIT->SCAN\n")); nstate = IEEE80211_S_SCAN; } @@ -5517,21 +5797,18 @@ iwm_newstate_task(void *psc) switch (nstate) { case IEEE80211_S_INIT: - sc->sc_scanband = 0; break; case IEEE80211_S_SCAN: - if (sc->sc_scanband) - break; - - if ((error = iwm_mvm_scan_request(sc, IEEE80211_CHAN_2GHZ, - ic->ic_des_esslen != 0, - ic->ic_des_essid, ic->ic_des_esslen)) != 0) { - printf("%s: could not initiate 2 GHz scan\n", - DEVNAME(sc)); - sc->sc_scanband = 0; + if (ic->ic_state == nstate && + (sc->sc_flags & IWM_FLAG_SCANNING)) + return; + error = iwm_mvm_lmac_scan(sc); + if (error != 0) { + printf("%s: could not initiate scan\n", DEVNAME(sc)); return; } + sc->sc_flags |= IWM_FLAG_SCANNING; ic->ic_state = nstate; iwm_led_blink_start(sc); return; @@ -5599,29 +5876,235 @@ iwm_endscan_cb(void *arg) { struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; - int done; DPRINTF(("scan ended\n")); - if (sc->sc_scanband == IEEE80211_CHAN_2GHZ && - sc->sc_nvm.sku_cap_band_52GHz_enable) { - int error; - done = 0; - if ((error = iwm_mvm_scan_request(sc, - IEEE80211_CHAN_5GHZ, ic->ic_des_esslen != 0, - ic->ic_des_essid, ic->ic_des_esslen)) != 0) { - printf("%s: could not initiate 5 GHz scan\n", - DEVNAME(sc)); - done = 1; + sc->sc_flags &= ~IWM_FLAG_SCANNING; + ieee80211_end_scan(&ic->ic_if); +} + +/* + * Aging and idle timeouts for the different possible scenarios + * in default configuration + */ +static const uint32_t +iwm_sf_full_timeout_def[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { + { + htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF), + htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_AGG_UNICAST_AGING_TIMER_DEF), + htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_MCAST_AGING_TIMER_DEF), + htole32(IWM_SF_MCAST_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_BA_AGING_TIMER_DEF), + htole32(IWM_SF_BA_IDLE_TIMER_DEF) + }, + { + htole32(IWM_SF_TX_RE_AGING_TIMER_DEF), + htole32(IWM_SF_TX_RE_IDLE_TIMER_DEF) + }, +}; + +/* + * Aging and idle timeouts for the different possible scenarios + * in single BSS MAC configuration. + */ +static const uint32_t +iwm_sf_full_timeout[IWM_SF_NUM_SCENARIO][IWM_SF_NUM_TIMEOUT_TYPES] = { + { + htole32(IWM_SF_SINGLE_UNICAST_AGING_TIMER), + htole32(IWM_SF_SINGLE_UNICAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_AGG_UNICAST_AGING_TIMER), + htole32(IWM_SF_AGG_UNICAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_MCAST_AGING_TIMER), + htole32(IWM_SF_MCAST_IDLE_TIMER) + }, + { + htole32(IWM_SF_BA_AGING_TIMER), + htole32(IWM_SF_BA_IDLE_TIMER) + }, + { + htole32(IWM_SF_TX_RE_AGING_TIMER), + htole32(IWM_SF_TX_RE_IDLE_TIMER) + }, +}; + +void +iwm_mvm_fill_sf_command(struct iwm_softc *sc, struct iwm_sf_cfg_cmd *sf_cmd, + struct ieee80211_node *ni) +{ + int i, j, watermark; + + sf_cmd->watermark[IWM_SF_LONG_DELAY_ON] = htole32(IWM_SF_W_MARK_SCAN); + + /* + * If we are in association flow - check antenna configuration + * capabilities of the AP station, and choose the watermark accordingly. + */ + if (ni) { + if (ni->ni_flags & IEEE80211_NODE_HT) { +#ifdef notyet + if (ni->ni_rxmcs[2] != 0) + watermark = IWM_SF_W_MARK_MIMO3; + else if (ni->ni_rxmcs[1] != 0) + watermark = IWM_SF_W_MARK_MIMO2; + else +#endif + watermark = IWM_SF_W_MARK_SISO; + } else { + watermark = IWM_SF_W_MARK_LEGACY; + } + /* default watermark value for unassociated mode. */ + } else { + watermark = IWM_SF_W_MARK_MIMO2; + } + sf_cmd->watermark[IWM_SF_FULL_ON] = htole32(watermark); + + for (i = 0; i < IWM_SF_NUM_SCENARIO; i++) { + for (j = 0; j < IWM_SF_NUM_TIMEOUT_TYPES; j++) { + sf_cmd->long_delay_timeouts[i][j] = + htole32(IWM_SF_LONG_DELAY_AGING_TIMER); } + } + + if (ni) { + memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout, + sizeof(iwm_sf_full_timeout)); } else { - done = 1; + memcpy(sf_cmd->full_on_timeouts, iwm_sf_full_timeout_def, + sizeof(iwm_sf_full_timeout_def)); } - if (done) { - ieee80211_end_scan(&ic->ic_if); - sc->sc_scanband = 0; +} + +int +iwm_mvm_sf_config(struct iwm_softc *sc, enum iwm_sf_state new_state) +{ + struct ieee80211com *ic = &sc->sc_ic; + struct iwm_sf_cfg_cmd sf_cmd = { + .state = htole32(IWM_SF_FULL_ON), + }; + int ret = 0; + + switch (new_state) { + case IWM_SF_UNINIT: + case IWM_SF_INIT_OFF: + iwm_mvm_fill_sf_command(sc, &sf_cmd, NULL); + break; + case IWM_SF_FULL_ON: + iwm_mvm_fill_sf_command(sc, &sf_cmd, ic->ic_bss); + break; + default: + DPRINTF(("Invalid state: %d. not sending Smart Fifo cmd\n", + new_state)); + return EINVAL; + } + + ret = iwm_mvm_send_cmd_pdu(sc, IWM_REPLY_SF_CFG_CMD, IWM_CMD_ASYNC, + sizeof(sf_cmd), &sf_cmd); + return ret; +} + +int +iwm_send_bt_init_conf(struct iwm_softc *sc) +{ + struct iwm_bt_coex_cmd bt_cmd; + + bt_cmd.mode = htole32(IWM_BT_COEX_WIFI); + bt_cmd.enabled_modules = htole32(IWM_BT_COEX_HIGH_BAND_RET); + + return iwm_mvm_send_cmd_pdu(sc, IWM_BT_CONFIG, 0, sizeof(bt_cmd), + &bt_cmd); +} + +int +iwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) +{ + struct iwm_mcc_update_cmd mcc_cmd; + struct iwm_host_cmd hcmd = { + .id = IWM_MCC_UPDATE_CMD, + .flags = (IWM_CMD_SYNC | IWM_CMD_WANT_SKB), + .data = { &mcc_cmd }, + }; + int ret; +#ifdef IWM_DEBUG + struct iwm_rx_packet *pkt; + struct iwm_mcc_update_resp_v1 *mcc_resp_v1 = NULL; + struct iwm_mcc_update_resp *mcc_resp; + int n_channels; + uint16_t mcc; +#endif + int resp_v2 = isset(sc->sc_enabled_capa, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); + + memset(&mcc_cmd, 0, sizeof(mcc_cmd)); + mcc_cmd.mcc = htole16(alpha2[0] << 8 | alpha2[1]); + if ((sc->sc_ucode_api & IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || + isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC)) + mcc_cmd.source_id = IWM_MCC_SOURCE_GET_CURRENT; + else + mcc_cmd.source_id = IWM_MCC_SOURCE_OLD_FW; + + if (resp_v2) + hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd); + else + hcmd.len[0] = sizeof(struct iwm_mcc_update_cmd_v1); + + DPRINTF(("send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], mcc_cmd.source_id)); + + ret = iwm_send_cmd(sc, &hcmd); + if (ret) + return ret; + +#ifdef IWM_DEBUG + pkt = hcmd.resp_pkt; + + /* Extract MCC response */ + if (resp_v2) { + mcc_resp = (void *)pkt->data; + mcc = mcc_resp->mcc; + n_channels = le32toh(mcc_resp->n_channels); + } else { + mcc_resp_v1 = (void *)pkt->data; + mcc = mcc_resp_v1->mcc; + n_channels = le32toh(mcc_resp_v1->n_channels); } + + /* W/A for a FW/NVM issue - returns 0x00 for the world domain */ + if (mcc == 0) + mcc = 0x3030; /* "00" - world */ + + DPRINTF(("%s: regulatory domain '%c%c' (%d channels available)\n", + DEVNAME(sc), mcc >> 8, mcc & 0xff, n_channels)); +#endif + iwm_free_resp(sc, &hcmd); + + return 0; +} + +void +iwm_mvm_tt_tx_backoff(struct iwm_softc *sc, uint32_t backoff) +{ + struct iwm_host_cmd cmd = { + .id = IWM_REPLY_THERMAL_MNG_BACKOFF, + .len = { sizeof(uint32_t), }, + .data = { &backoff, }, + }; + + if (iwm_send_cmd(sc, &cmd) != 0) + DPRINTF(("%s: failed to change thermal tx backoff\n", + DEVNAME(sc))); } int @@ -5629,7 +6112,7 @@ iwm_init_hw(struct iwm_softc *sc) { struct ieee80211com *ic = &sc->sc_ic; struct iwm_node *in = (struct iwm_node *)ic->ic_bss; - int error, i, qid; + int error, i, ac; if ((error = iwm_preinit(sc)) != 0) return error; @@ -5658,7 +6141,10 @@ iwm_init_hw(struct iwm_softc *sc) goto error; } - if ((error = iwm_send_tx_ant_cfg(sc, IWM_FW_VALID_TX_ANT(sc))) != 0) + if ((error = iwm_send_bt_init_conf(sc)) != 0) + goto error; + + if ((error = iwm_send_tx_ant_cfg(sc, iwm_fw_valid_tx_ant(sc))) != 0) goto error; /* Send phy db control command and then phy db calibration*/ @@ -5683,13 +6169,24 @@ iwm_init_hw(struct iwm_softc *sc) goto error; } + /* Initialize tx backoffs to the minimum. */ + iwm_mvm_tt_tx_backoff(sc, 0); + error = iwm_mvm_power_update_device(sc); if (error) goto error; - /* Mark TX rings as active. */ - for (qid = 0; qid < 4; qid++) { - iwm_enable_txq(sc, qid, qid); + if (isset(sc->sc_enabled_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT)) { + if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) + goto error; + } + + /* Enable Tx queues. */ + for (ac = 0; ac < EDCA_NUM_AC; ac++) { + error = iwm_enable_txq(sc, IWM_STATION_ID, ac, + iwm_mvm_ac_to_tx_fifo[ac]); + if (error) + goto error; } /* Add the MAC context. */ @@ -5698,6 +6195,11 @@ iwm_init_hw(struct iwm_softc *sc) goto error; } + if ((error = iwm_mvm_disable_beacon_filter(sc)) != 0) { + printf("%s: failed to disable beacon filter\n", DEVNAME(sc)); + goto error; + } + return 0; error: @@ -5845,7 +6347,6 @@ iwm_stop(struct ifnet *ifp, int disable) sc->sc_flags &= ~IWM_FLAG_HW_INITED; sc->sc_flags |= IWM_FLAG_STOPPED; sc->sc_generation++; - sc->sc_scanband = 0; ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED; ifp->if_flags &= ~IFF_RUNNING; ifq_clr_oactive(&ifp->if_snd); @@ -5968,8 +6469,8 @@ iwm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) struct iwm_error_event_table { uint32_t valid; /* (nonzero) valid, (0) log is empty */ uint32_t error_id; /* type of error */ - uint32_t pc; /* program counter */ - uint32_t blink1; /* branch link */ + uint32_t trm_hw_status0; /* TRM HW status */ + uint32_t trm_hw_status1; /* TRM HW status */ uint32_t blink2; /* branch link */ uint32_t ilink1; /* interrupt link */ uint32_t ilink2; /* interrupt link */ @@ -5981,8 +6482,9 @@ struct iwm_error_event_table { uint32_t tsf_hi; /* network timestamp function timer */ uint32_t gp1; /* GP1 timer register */ uint32_t gp2; /* GP2 timer register */ - uint32_t gp3; /* GP3 timer register */ - uint32_t ucode_ver; /* uCode version */ + uint32_t fw_rev_type; /* firmware revision type */ + uint32_t major; /* uCode version major */ + uint32_t minor; /* uCode version minor */ uint32_t hw_ver; /* HW Silicon version */ uint32_t brd_ver; /* HW board version */ uint32_t log_pc; /* log program counter */ @@ -5999,7 +6501,7 @@ struct iwm_error_event_table { * time_flag */ uint32_t isr4; /* isr status register LMPM_NIC_ISR4: * wico interrupt */ - uint32_t isr_pref; /* isr status register LMPM_NIC_PREF_STAT */ + uint32_t last_cmd_id; /* last HCMD id handled by the firmware */ uint32_t wait_event; /* wait event() caller address */ uint32_t l2p_control; /* L2pControlField */ uint32_t l2p_duration; /* L2pDurationField */ @@ -6065,13 +6567,13 @@ iwm_nic_error(struct iwm_softc *sc) printf("%s: dumping device error log\n", DEVNAME(sc)); base = sc->sc_uc.uc_error_event_table; - if (base < 0x800000 || base >= 0x80C000) { - printf("%s: Not valid error log pointer 0x%08x\n", + if (base < 0x800000) { + printf("%s: Invalid error log pointer 0x%08x\n", DEVNAME(sc), base); return; } - if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t)) != 0) { + if (iwm_read_mem(sc, base, &table, sizeof(table)/sizeof(uint32_t))) { printf("%s: reading errlog failed\n", DEVNAME(sc)); return; } @@ -6082,15 +6584,17 @@ iwm_nic_error(struct iwm_softc *sc) } if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { - printf("%s: Start IWL Error Log Dump:\n", DEVNAME(sc)); + printf("%s: Start Error Log Dump:\n", DEVNAME(sc)); printf("%s: Status: 0x%x, count: %d\n", DEVNAME(sc), sc->sc_flags, table.valid); } printf("%s: 0x%08X | %-28s\n", DEVNAME(sc), table.error_id, - iwm_desc_lookup(table.error_id)); - printf("%s: %08X | uPc\n", DEVNAME(sc), table.pc); - printf("%s: %08X | branchlink1\n", DEVNAME(sc), table.blink1); + iwm_desc_lookup(table.error_id)); + printf("%s: %08X | trm_hw_status0\n", DEVNAME(sc), + table.trm_hw_status0); + printf("%s: %08X | trm_hw_status1\n", DEVNAME(sc), + table.trm_hw_status1); printf("%s: %08X | branchlink2\n", DEVNAME(sc), table.blink2); printf("%s: %08X | interruptlink1\n", DEVNAME(sc), table.ilink1); printf("%s: %08X | interruptlink2\n", DEVNAME(sc), table.ilink2); @@ -6102,8 +6606,12 @@ iwm_nic_error(struct iwm_softc *sc) printf("%s: %08X | tsf hi\n", DEVNAME(sc), table.tsf_hi); printf("%s: %08X | time gp1\n", DEVNAME(sc), table.gp1); printf("%s: %08X | time gp2\n", DEVNAME(sc), table.gp2); - printf("%s: %08X | time gp3\n", DEVNAME(sc), table.gp3); - printf("%s: %08X | uCode version\n", DEVNAME(sc), table.ucode_ver); + printf("%s: %08X | uCode revision type\n", DEVNAME(sc), + table.fw_rev_type); + printf("%s: %08X | uCode version major\n", DEVNAME(sc), + table.major); + printf("%s: %08X | uCode version minor\n", DEVNAME(sc), + table.minor); printf("%s: %08X | hw version\n", DEVNAME(sc), table.hw_ver); printf("%s: %08X | board version\n", DEVNAME(sc), table.brd_ver); printf("%s: %08X | hcmd\n", DEVNAME(sc), table.hcmd); @@ -6112,7 +6620,7 @@ iwm_nic_error(struct iwm_softc *sc) printf("%s: %08X | isr2\n", DEVNAME(sc), table.isr2); printf("%s: %08X | isr3\n", DEVNAME(sc), table.isr3); printf("%s: %08X | isr4\n", DEVNAME(sc), table.isr4); - printf("%s: %08X | isr_pref\n", DEVNAME(sc), table.isr_pref); + printf("%s: %08X | last cmd Id\n", DEVNAME(sc), table.last_cmd_id); printf("%s: %08X | wait_event\n", DEVNAME(sc), table.wait_event); printf("%s: %08X | l2p_control\n", DEVNAME(sc), table.l2p_control); printf("%s: %08X | l2p_duration\n", DEVNAME(sc), table.l2p_duration); @@ -6157,7 +6665,7 @@ iwm_notif_intr(struct iwm_softc *sc) struct iwm_rx_data *data = &sc->rxq.data[sc->rxq.cur]; struct iwm_rx_packet *pkt; struct iwm_cmd_response *cresp; - int qid, idx; + int qid, idx, code; bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(*pkt), BUS_DMASYNC_POSTREAD); @@ -6166,9 +6674,9 @@ iwm_notif_intr(struct iwm_softc *sc) qid = pkt->hdr.qid & ~0x80; idx = pkt->hdr.idx; - DPRINTFN(12, ("rx packet qid=%d idx=%d flags=%x type=%x %d %d\n", - pkt->hdr.qid & ~0x80, pkt->hdr.idx, pkt->hdr.flags, - pkt->hdr.code, sc->rxq.cur, hw)); + code = IWM_WIDE_ID(pkt->hdr.flags, pkt->hdr.code); + DPRINTFN(12, ("rx packet qid=%d idx=%d type=%x %d %d\n", + pkt->hdr.qid & ~0x80, pkt->hdr.idx, code, sc->rxq.cur, hw)); /* * randomly get these from the firmware, no idea why. @@ -6180,7 +6688,7 @@ iwm_notif_intr(struct iwm_softc *sc) continue; } - switch (pkt->hdr.code) { + switch (code) { case IWM_REPLY_RX_PHY_CMD: iwm_mvm_rx_rx_phy_cmd(sc, pkt, data); break; @@ -6197,16 +6705,52 @@ iwm_notif_intr(struct iwm_softc *sc) /* OpenBSD does not provide ieee80211_beacon_miss() */ break; + case IWM_MFUART_LOAD_NOTIFICATION: + break; + case IWM_MVM_ALIVE: { - struct iwm_mvm_alive_resp *resp; - SYNC_RESP_STRUCT(resp, pkt); + struct iwm_mvm_alive_resp_v1 *resp1; + struct iwm_mvm_alive_resp_v2 *resp2; + struct iwm_mvm_alive_resp_v3 *resp3; + + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp1)) { + SYNC_RESP_STRUCT(resp1, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp1->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp1->log_event_table_ptr); + sc->sched_base = le32toh(resp1->scd_base_ptr); + if (resp1->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } + + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp2)) { + SYNC_RESP_STRUCT(resp2, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp2->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp2->log_event_table_ptr); + sc->sched_base = le32toh(resp2->scd_base_ptr); + if (resp2->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } - sc->sc_uc.uc_error_event_table - = le32toh(resp->error_event_table_ptr); - sc->sc_uc.uc_log_event_table - = le32toh(resp->log_event_table_ptr); - sc->sched_base = le32toh(resp->scd_base_ptr); - sc->sc_uc.uc_ok = resp->status == IWM_ALIVE_STATUS_OK; + if (iwm_rx_packet_payload_len(pkt) == sizeof(*resp3)) { + SYNC_RESP_STRUCT(resp3, pkt); + sc->sc_uc.uc_error_event_table + = le32toh(resp3->error_event_table_ptr); + sc->sc_uc.uc_log_event_table + = le32toh(resp3->log_event_table_ptr); + sc->sched_base = le32toh(resp3->scd_base_ptr); + if (resp3->status == IWM_ALIVE_STATUS_OK) + sc->sc_uc.uc_ok = 1; + else + sc->sc_uc.uc_ok = 0; + } sc->sc_uc.uc_intr = 1; wakeup(&sc->sc_uc); @@ -6228,6 +6772,7 @@ iwm_notif_intr(struct iwm_softc *sc) break; } case IWM_NVM_ACCESS_CMD: + case IWM_MCC_UPDATE_CMD: if (sc->sc_wantresp == ((qid << 16) | idx)) { bus_dmamap_sync(sc->sc_dmat, data->map, 0, sizeof(sc->sc_cmd_resp), @@ -6237,6 +6782,20 @@ iwm_notif_intr(struct iwm_softc *sc) } break; + case IWM_MCC_CHUB_UPDATE_CMD: { + struct iwm_mcc_chub_notif *notif; + SYNC_RESP_STRUCT(notif, pkt); + + sc->sc_fw_mcc[0] = (notif->mcc & 0xff00) >> 8; + sc->sc_fw_mcc[1] = notif->mcc & 0xff; + sc->sc_fw_mcc[2] = '\0'; + DPRINTF(("%s: fw source %d sent CC '%s'\n", + DEVNAME(sc), notif->source_id, sc->sc_fw_mcc)); + } + + case IWM_DTS_MEASUREMENT_NOTIFICATION: + break; + case IWM_PHY_CONFIGURATION_CMD: case IWM_TX_ANT_CONFIGURATION_CMD: case IWM_ADD_STA: @@ -6247,12 +6806,15 @@ iwm_notif_intr(struct iwm_softc *sc) case IWM_BINDING_CONTEXT_CMD: case IWM_TIME_EVENT_CMD: case IWM_SCAN_REQUEST_CMD: + case IWM_SCAN_OFFLOAD_REQUEST_CMD: case IWM_REPLY_BEACON_FILTERING_CMD: case IWM_MAC_PM_POWER_TABLE: case IWM_TIME_QUOTA_CMD: case IWM_REMOVE_STA: case IWM_TXPATH_FLUSH: case IWM_LQ_CMD: + case IWM_BT_CONFIG: + case IWM_REPLY_THERMAL_MNG_BACKOFF: SYNC_RESP_STRUCT(cresp, pkt); if (sc->sc_wantresp == ((qid << 16) | idx)) { memcpy(sc->sc_cmd_resp, @@ -6269,8 +6831,14 @@ iwm_notif_intr(struct iwm_softc *sc) wakeup(&sc->sc_init_complete); break; - case IWM_SCAN_COMPLETE_NOTIFICATION: { - struct iwm_scan_complete_notif *notif; + case IWM_SCAN_OFFLOAD_COMPLETE: { + struct iwm_periodic_scan_complete *notif; + SYNC_RESP_STRUCT(notif, pkt); + + break; } + + case IWM_SCAN_ITERATION_COMPLETE: { + struct iwm_lmac_scan_complete_notif *notif; SYNC_RESP_STRUCT(notif, pkt); task_add(sc->sc_eswq, &sc->sc_eswk); @@ -6296,6 +6864,17 @@ iwm_notif_intr(struct iwm_softc *sc) case IWM_MCAST_FILTER_CMD: break; + case IWM_SCD_QUEUE_CFG: { + struct iwm_scd_txq_cfg_rsp *rsp; + SYNC_RESP_STRUCT(rsp, pkt); + + DPRINTF(("%s: queue cfg token=0x%x sta_id=%d " + "tid=%d scd_queue=%d\n", + DEVNAME(sc), rsp->token, rsp->sta_id, rsp->tid, + rsp->scd_queue)); + break; + } + default: printf("%s: unhandled firmware response 0x%x/0x%x " "rx ring %d[%d]\n", @@ -6535,12 +7114,9 @@ iwm_preinit(struct iwm_softc *sc) /* Print version info and MAC address on first successful fw load. */ attached = 1; - printf("%s: hw rev 0x%x, fw ver %d.%d (API ver %d), address %s\n", + printf("%s: hw rev 0x%x, fw ver %s, address %s\n", DEVNAME(sc), sc->sc_hw_rev & IWM_CSR_HW_REV_TYPE_MSK, - IWM_UCODE_MAJOR(sc->sc_fwver), - IWM_UCODE_MINOR(sc->sc_fwver), - IWM_UCODE_API(sc->sc_fwver), - ether_sprintf(sc->sc_nvm.hw_addr)); + sc->sc_fwver, ether_sprintf(sc->sc_nvm.hw_addr)); if (sc->sc_nvm.sku_cap_11n_enable) iwm_setup_ht_rates(sc); @@ -6651,17 +7227,17 @@ iwm_attach(struct device *parent, struct device *self, void *aux) switch (PCI_PRODUCT(pa->pa_id)) { case PCI_PRODUCT_INTEL_WL_3160_1: case PCI_PRODUCT_INTEL_WL_3160_2: - sc->sc_fwname = "iwm-3160-9"; + sc->sc_fwname = "iwm-3160-16"; sc->host_interrupt_operation_mode = 1; break; case PCI_PRODUCT_INTEL_WL_7260_1: case PCI_PRODUCT_INTEL_WL_7260_2: - sc->sc_fwname = "iwm-7260-9"; + sc->sc_fwname = "iwm-7260-16"; sc->host_interrupt_operation_mode = 1; break; case PCI_PRODUCT_INTEL_WL_7265_1: case PCI_PRODUCT_INTEL_WL_7265_2: - sc->sc_fwname = "iwm-7265-9"; + sc->sc_fwname = "iwm-7265-16"; sc->host_interrupt_operation_mode = 0; break; default: @@ -6741,6 +7317,7 @@ iwm_attach(struct device *parent, struct device *self, void *aux) IEEE80211_C_WEP | /* WEP */ IEEE80211_C_RSN | /* WPA/RSN */ IEEE80211_C_SCANALL | /* device scans all channels at once */ + IEEE80211_C_SCANALLBAND | /* device scans all bands at once */ IEEE80211_C_SHSLOT | /* short slot time supported */ IEEE80211_C_SHPREAMBLE; /* short preamble supported */ diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h index 528e2a3aced..872b95f10f4 100644 --- a/sys/dev/pci/if_iwmreg.h +++ b/sys/dev/pci/if_iwmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmreg.h,v 1.11 2016/05/18 07:28:01 stsp Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.12 2016/05/25 13:35:12 stsp Exp $ */ /****************************************************************************** * @@ -126,6 +126,9 @@ #define IWM_CSR_UCODE_DRV_GP1_CLR (0x05c) #define IWM_CSR_UCODE_DRV_GP2 (0x060) +#define IWM_CSR_MBOX_SET_REG (0x088) +#define IWM_CSR_MBOX_SET_REG_OS_ALIVE 0x20 + #define IWM_CSR_LED_REG (0x094) #define IWM_CSR_DRAM_INT_TBL_REG (0x0A0) #define IWM_CSR_MAC_SHADOW_REG_CTRL (0x0A8) /* 6000 and up */ @@ -172,6 +175,8 @@ #define IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_READY (0x00400000) /* PCI_OWN_SEM */ #define IWM_CSR_HW_IF_CONFIG_REG_BIT_NIC_PREPARE_DONE (0x02000000) /* ME_OWN */ #define IWM_CSR_HW_IF_CONFIG_REG_PREPARE (0x08000000) /* WAKE_ME */ +#define IWM_CSR_HW_IF_CONFIG_REG_ENABLE_PME (0x10000000) +#define IWM_CSR_HW_IF_CONFIG_REG_PERSIST_MODE (0x40000000) /* PERSISTENCE */ #define IWM_CSR_INT_PERIODIC_DIS (0x00) /* disable periodic int*/ #define IWM_CSR_INT_PERIODIC_ENA (0xFF) /* 255*32 usec ~ 8 msec*/ @@ -298,6 +303,7 @@ #define IWM_CSR_HW_REV_TYPE_2x00 (0x0000100) #define IWM_CSR_HW_REV_TYPE_105 (0x0000110) #define IWM_CSR_HW_REV_TYPE_135 (0x0000120) +#define IWM_CSR_HW_REV_TYPE_7265D (0x0000210) #define IWM_CSR_HW_REV_TYPE_NONE (0x00001F0) /* EEPROM REG */ @@ -392,6 +398,7 @@ /* DRAM INT TABLE */ #define IWM_CSR_DRAM_INT_TBL_ENABLE (1 << 31) +#define IWM_CSR_DRAM_INIT_TBL_WRITE_POINTER (1 << 28) #define IWM_CSR_DRAM_INIT_TBL_WRAP_CHECK (1 << 27) /* SECURE boot registers */ @@ -411,19 +418,37 @@ enum iwm_secure_boot_status_reg { IWM_CSR_SECURE_BOOT_CPU_STATUS_SIGN_VERF_FAIL = 0x00000010, }; -#define IWM_CSR_UCODE_LOAD_STATUS_ADDR (0x100) +#define IWM_FH_UCODE_LOAD_STATUS 0x1af0 +#define IWM_CSR_UCODE_LOAD_STATUS_ADDR 0x1e70 enum iwm_secure_load_status_reg { - IWM_CSR_CPU_STATUS_LOADING_STARTED = 0x00000001, - IWM_CSR_CPU_STATUS_LOADING_COMPLETED = 0x00000002, - IWM_CSR_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, - IWM_CSR_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, + IWM_LMPM_CPU_UCODE_LOADING_STARTED = 0x00000001, + IWM_LMPM_CPU_HDRS_LOADING_COMPLETED = 0x00000003, + IWM_LMPM_CPU_UCODE_LOADING_COMPLETED = 0x00000007, + IWM_LMPM_CPU_STATUS_NUM_OF_LAST_COMPLETED = 0x000000F8, + IWM_LMPM_CPU_STATUS_NUM_OF_LAST_LOADED_BLOCK = 0x0000FF00, }; +#define IWM_FH_MEM_TB_MAX_LENGTH 0x20000 + +#define IWM_LMPM_SECURE_INSPECTOR_CODE_ADDR 0x1e38 +#define IWM_LMPM_SECURE_INSPECTOR_DATA_ADDR 0x1e3c +#define IWM_LMPM_SECURE_UCODE_LOAD_CPU1_HDR_ADDR 0x1e78 +#define IWM_LMPM_SECURE_UCODE_LOAD_CPU2_HDR_ADDR 0x1e7c -#define IWM_CSR_SECURE_INSPECTOR_CODE_ADDR (0x100) -#define IWM_CSR_SECURE_INSPECTOR_DATA_ADDR (0x100) +#define IWM_LMPM_SECURE_INSPECTOR_CODE_MEM_SPACE 0x400000 +#define IWM_LMPM_SECURE_INSPECTOR_DATA_MEM_SPACE 0x402000 +#define IWM_LMPM_SECURE_CPU1_HDR_MEM_SPACE 0x420000 +#define IWM_LMPM_SECURE_CPU2_HDR_MEM_SPACE 0x420400 #define IWM_CSR_SECURE_TIME_OUT (100) +/* extended range in FW SRAM */ +#define IWM_FW_MEM_EXTENDED_START 0x40000 +#define IWM_FW_MEM_EXTENDED_END 0x57FFF + +/* FW chicken bits */ +#define IWM_LMPM_CHICK 0xa01ff8 +#define IWM_LMPM_CHICK_EXTENDED_ADDR_SPACE 0x01 + #define IWM_FH_TCSR_0_REG0 (0x1D00) /* @@ -474,6 +499,32 @@ enum iwm_secure_load_status_reg { #define IWM_HBUS_TARG_PRPH_WDAT (IWM_HBUS_BASE+0x04c) #define IWM_HBUS_TARG_PRPH_RDAT (IWM_HBUS_BASE+0x050) +/* enable the ID buf for read */ +#define IWM_WFPM_PS_CTL_CLR 0xa0300c +#define IWM_WFMP_MAC_ADDR_0 0xa03080 +#define IWM_WFMP_MAC_ADDR_1 0xa03084 +#define IWM_LMPM_PMG_EN 0xa01cec +#define IWM_RADIO_REG_SYS_MANUAL_DFT_0 0xad4078 +#define IWM_RFIC_REG_RD 0xad0470 +#define IWM_WFPM_CTRL_REG 0xa03030 +#define IWM_WFPM_AUX_CTL_AUX_IF_MAC_OWNER_MSK 0x08000000 +#define IWM_ENABLE_WFPM 0x80000000 + +#define IWM_AUX_MISC_REG 0xa200b0 +#define IWM_HW_STEP_LOCATION_BITS 24 + +#define IWM_AUX_MISC_MASTER1_EN 0xa20818 +#define IWM_AUX_MISC_MASTER1_EN_SBE_MSK 0x1 +#define IWM_AUX_MISC_MASTER1_SMPHR_STATUS 0xa20800 +#define IWM_RSA_ENABLE 0xa24b08 +#define IWM_PREG_AUX_BUS_WPROT_0 0xa04cc0 +#define IWM_SB_CFG_OVERRIDE_ADDR 0xa26c78 +#define IWM_SB_CFG_OVERRIDE_ENABLE 0x8000 +#define IWM_SB_CFG_BASE_OVERRIDE 0xa20000 +#define IWM_SB_MODIFY_CFG_FLAG 0xa03088 +#define IWM_SB_CPU_1_STATUS 0xa01e30 +#define IWM_SB_CPU_2_STATUS 0Xa01e34 + /* Used to enable DBGM */ #define IWM_HBUS_TARG_TEST_REG (IWM_HBUS_BASE+0x05c) @@ -549,7 +600,12 @@ enum iwm_dtd_diode_reg { * containing CAM (Continuous Active Mode) indication. * @IWM_UCODE_TLV_FLAGS_P2P_PS: P2P client power save is supported (only on a * single bound interface). + * @IWM_UCODE_TLV_FLAGS_UAPSD_SUPPORT: General support for uAPSD + * @IWM_UCODE_TLV_FLAGS_EBS_SUPPORT: this uCode image supports EBS. * @IWM_UCODE_TLV_FLAGS_P2P_PS_UAPSD: P2P client supports uAPSD power save + * @IWM_UCODE_TLV_FLAGS_BCAST_FILTERING: uCode supports broadcast filtering. + * @IWM_UCODE_TLV_FLAGS_GO_UAPSD: AP/GO interfaces support uAPSD clients + * */ enum iwm_ucode_tlv_flag { IWM_UCODE_TLV_FLAGS_PAN = (1 << 0), @@ -572,8 +628,146 @@ enum iwm_ucode_tlv_flag { IWM_UCODE_TLV_FLAGS_STA_KEY_CMD = (1 << 19), IWM_UCODE_TLV_FLAGS_DEVICE_PS_CMD = (1 << 20), IWM_UCODE_TLV_FLAGS_P2P_PS = (1 << 21), + IWM_UCODE_TLV_FLAGS_BSS_P2P_PS_DCM = (1 << 22), + IWM_UCODE_TLV_FLAGS_BSS_P2P_PS_SCM = (1 << 23), IWM_UCODE_TLV_FLAGS_UAPSD_SUPPORT = (1 << 24), + IWM_UCODE_TLV_FLAGS_EBS_SUPPORT = (1 << 25), IWM_UCODE_TLV_FLAGS_P2P_PS_UAPSD = (1 << 26), + IWM_UCODE_TLV_FLAGS_BCAST_FILTERING = (1 << 29), + IWM_UCODE_TLV_FLAGS_GO_UAPSD = (1 << 30), + IWM_UCODE_TLV_FLAGS_LTE_COEX = (1 << 31), +}; +#define IWM_UCODE_TLV_FLAG_BITS \ + "\020\1PAN\2NEWSCAN\3MFP\4P2P\5DW_BC_TABLE\6NEWBT_COEX\7PM_CMD\10SHORT_BL\11RX_ENERGY\12TIME_EVENT_V2\13D3_6_IPV6\14BF_UPDATED\15NO_BASIC_SSID\17D3_CONTINUITY\20NEW_NSOFFL_S\21NEW_NSOFFL_L\22SCHED_SCAN\24STA_KEY_CMD\25DEVICE_PS_CMD\26P2P_PS\27P2P_PS_DCM\30P2P_PS_SCM\31UAPSD_SUPPORT\32EBS\33P2P_PS_UAPSD\36BCAST_FILTERING\37GO_UAPSD\40LTE_COEX" + +/** + * enum iwm_ucode_tlv_api - ucode api + * @IWM_UCODE_TLV_API_FRAGMENTED_SCAN: This ucode supports active dwell time + * longer than the passive one, which is essential for fragmented scan. + * @IWM_UCODE_TLV_API_WIFI_MCC_UPDATE: ucode supports MCC updates with source. + * @IWM_UCODE_TLV_API_WIDE_CMD_HDR: ucode supports wide command header + * @IWM_UCODE_TLV_API_LQ_SS_PARAMS: Configure STBC/BFER via LQ CMD ss_params + * @IWM_UCODE_TLV_API_EXT_SCAN_PRIORITY: scan APIs use 8-level priority + * instead of 3. + * @IWM_UCODE_TLV_API_TX_POWER_CHAIN: TX power API has larger command size + * (command version 3) that supports per-chain limits + * + * @IWM_NUM_UCODE_TLV_API: number of bits used + */ +enum iwm_ucode_tlv_api { + IWM_UCODE_TLV_API_FRAGMENTED_SCAN = (1 << 8), + IWM_UCODE_TLV_API_WIFI_MCC_UPDATE = (1 << 9), + IWM_UCODE_TLV_API_WIDE_CMD_HDR = (1 << 14), + IWM_UCODE_TLV_API_LQ_SS_PARAMS = (1 << 18), + IWM_UCODE_TLV_API_EXT_SCAN_PRIORITY = (1 << 24), + IWM_UCODE_TLV_API_TX_POWER_CHAIN = (1 << 27), + + IWM_NUM_UCODE_TLV_API = 32 +}; + +#define IWM_UCODE_TLV_API_BITS \ + "\020\10FRAGMENTED_SCAN\11WIFI_MCC_UPDATE\16WIDE_CMD_HDR\22LQ_SS_PARAMS\30EXT_SCAN_PRIO\33TX_POWER_CHAIN" + +/** + * enum iwm_ucode_tlv_capa - ucode capabilities + * @IWM_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 + * @IWM_UCODE_TLV_CAPA_LAR_SUPPORT: supports Location Aware Regulatory + * @IWM_UCODE_TLV_CAPA_UMAC_SCAN: supports UMAC scan. + * @IWM_UCODE_TLV_CAPA_BEAMFORMER: supports Beamformer + * @IWM_UCODE_TLV_CAPA_TOF_SUPPORT: supports Time of Flight (802.11mc FTM) + * @IWM_UCODE_TLV_CAPA_TDLS_SUPPORT: support basic TDLS functionality + * @IWM_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT: supports insertion of current + * tx power value into TPC Report action frame and Link Measurement Report + * action frame + * @IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT: supports updating current + * channel in DS parameter set element in probe requests. + * @IWM_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT: supports adding TPC Report IE in + * probe requests. + * @IWM_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT: supports Quiet Period requests + * @IWM_UCODE_TLV_CAPA_DQA_SUPPORT: supports dynamic queue allocation (DQA), + * which also implies support for the scheduler configuration command + * @IWM_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH: supports TDLS channel switching + * @IWM_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG: Consolidated D3-D0 image + * @IWM_UCODE_TLV_CAPA_HOTSPOT_SUPPORT: supports Hot Spot Command + * @IWM_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command + * @IWM_UCODE_TLV_CAPA_2G_COEX_SUPPORT: supports 2G coex Command + * @IWM_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload + * @IWM_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics + * @IWM_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD: support p2p standalone U-APSD + * @IWM_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running + * @IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different + * sources for the MCC. This TLV bit is a future replacement to + * IWM_UCODE_TLV_API_WIFI_MCC_UPDATE. When either is set, multi-source LAR + * is supported. + * @IWM_UCODE_TLV_CAPA_BT_COEX_RRC: supports BT Coex RRC + * @IWM_UCODE_TLV_CAPA_GSCAN_SUPPORT: supports gscan + * @IWM_UCODE_TLV_CAPA_NAN_SUPPORT: supports NAN + * @IWM_UCODE_TLV_CAPA_UMAC_UPLOAD: supports upload mode in umac (1=supported, + * 0=no support) + * @IWM_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement + * @IWM_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts + * @IWM_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT + * @IWM_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what + * antenna the beacon should be transmitted + * @IWM_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon + * from AP and will send it upon d0i3 exit. + * @IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2 + * @IWM_UCODE_TLV_CAPA_CT_KILL_BY_FW: firmware responsible for CT-kill + * @IWM_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT: supports temperature + * thresholds reporting + * @IWM_UCODE_TLV_CAPA_CTDP_SUPPORT: supports cTDP command + * @IWM_UCODE_TLV_CAPA_USNIFFER_UNIFIED: supports usniffer enabled in + * regular image. + * @IWM_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG: support getting more shared + * memory addresses from the firmware. + * @IWM_UCODE_TLV_CAPA_LQM_SUPPORT: supports Link Quality Measurement + * @IWM_UCODE_TLV_CAPA_LMAC_UPLOAD: supports upload mode in lmac (1=supported, + * 0=no support) + * + * @IWM_NUM_UCODE_TLV_CAPA: number of bits used + */ +enum iwm_ucode_tlv_capa { + IWM_UCODE_TLV_CAPA_D0I3_SUPPORT = 0, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT = 1, + IWM_UCODE_TLV_CAPA_UMAC_SCAN = 2, + IWM_UCODE_TLV_CAPA_BEAMFORMER = 3, + IWM_UCODE_TLV_CAPA_TOF_SUPPORT = 5, + IWM_UCODE_TLV_CAPA_TDLS_SUPPORT = 6, + IWM_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT = 8, + IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT = 9, + IWM_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT = 10, + IWM_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT = 11, + IWM_UCODE_TLV_CAPA_DQA_SUPPORT = 12, + IWM_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH = 13, + IWM_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG = 17, + IWM_UCODE_TLV_CAPA_HOTSPOT_SUPPORT = 18, + IWM_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = 19, + IWM_UCODE_TLV_CAPA_2G_COEX_SUPPORT = 20, + IWM_UCODE_TLV_CAPA_CSUM_SUPPORT = 21, + IWM_UCODE_TLV_CAPA_RADIO_BEACON_STATS = 22, + IWM_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD = 26, + IWM_UCODE_TLV_CAPA_BT_COEX_PLCR = 28, + IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC = 29, + IWM_UCODE_TLV_CAPA_BT_COEX_RRC = 30, + IWM_UCODE_TLV_CAPA_GSCAN_SUPPORT = 31, + IWM_UCODE_TLV_CAPA_NAN_SUPPORT = 34, + IWM_UCODE_TLV_CAPA_UMAC_UPLOAD = 35, + IWM_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = 64, + IWM_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = 65, + IWM_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = 67, + IWM_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = 68, + IWM_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = 71, + IWM_UCODE_TLV_CAPA_BEACON_STORING = 72, + IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = 73, + IWM_UCODE_TLV_CAPA_CT_KILL_BY_FW = 74, + IWM_UCODE_TLV_CAPA_TEMP_THS_REPORT_SUPPORT = 75, + IWM_UCODE_TLV_CAPA_CTDP_SUPPORT = 76, + IWM_UCODE_TLV_CAPA_USNIFFER_UNIFIED = 77, + IWM_UCODE_TLV_CAPA_LMAC_UPLOAD = 79, + IWM_UCODE_TLV_CAPA_EXTEND_SHARED_MEM_CFG = 80, + IWM_UCODE_TLV_CAPA_LQM_SUPPORT = 81, + + IWM_NUM_UCODE_TLV_CAPA = 128 }; /* The default calibrate table size if not specified by firmware file */ @@ -599,8 +793,8 @@ enum iwm_ucode_sec { * For 16.0 uCode and above, there is no differentiation between sections, * just an offset to the HW address. */ -#define IWM_UCODE_SECTION_MAX 6 -#define IWM_UCODE_FIRST_SECTION_OF_SECOND_CPU (IWM_UCODE_SECTION_MAX/2) +#define IWM_CPU1_CPU2_SEPARATOR_SECTION 0xFFFFCCCC +#define IWM_PAGING_SEPARATOR_SECTION 0xAAAABBBB /* uCode version contains 4 values: Major/Minor/API/Serial */ #define IWM_UCODE_MAJOR(ver) (((ver) & 0xFF000000) >> 24) @@ -739,7 +933,17 @@ enum iwm_ucode_tlv_type { * handling ucode version 9. */ IWM_UCODE_TLV_API_CHANGES_SET = 29, - IWM_UCODE_TLV_ENABLED_CAPABILITIES = 30 + IWM_UCODE_TLV_ENABLED_CAPABILITIES = 30, + + IWM_UCODE_TLV_N_SCAN_CHANNELS = 31, + IWM_UCODE_TLV_PAGING = 32, + IWM_UCODE_TLV_SEC_RT_USNIFFER = 34, + IWM_UCODE_TLV_SDIO_ADMA_ADDR = 35, + IWM_UCODE_TLV_FW_VERSION = 36, + IWM_UCODE_TLV_FW_DBG_DEST = 38, + IWM_UCODE_TLV_FW_DBG_CONF = 39, + IWM_UCODE_TLV_FW_DBG_TRIGGER = 40, + IWM_UCODE_TLV_FW_GSCAN_CAPA = 50, }; struct iwm_ucode_tlv { @@ -748,6 +952,16 @@ struct iwm_ucode_tlv { uint8_t data[0]; }; +struct iwm_ucode_api { + uint32_t api_index; + uint32_t api_flags; +} __packed; + +struct iwm_ucode_capa { + uint32_t api_index; + uint32_t api_capa; +} __packed; + #define IWM_TLV_UCODE_MAGIC 0x0a4c5749 struct iwm_tlv_ucode_header { @@ -812,7 +1026,9 @@ struct iwm_tlv_ucode_header { #define IWM_DEVICE_SYSTEM_TIME_REG 0xA0206C /* Device NMI register */ -#define IWM_DEVICE_SET_NMI_REG 0x00a01c30 +#define IWM_DEVICE_SET_NMI_REG 0x00a01c30 +#define IWM_DEVICE_SET_NMI_VAL_HW 0x01 +#define IWM_DEVICE_SET_NMI_VAL_DRV 0x80 /***************************************************************************** * 7000/3000 series SHR DTS addresses * @@ -926,6 +1142,8 @@ struct iwm_tlv_ucode_header { #define IWM_SCD_QUEUE_CTX_REG2_WIN_SIZE_MSK (0x0000007F) #define IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_POS (16) #define IWM_SCD_QUEUE_CTX_REG2_FRAME_LIMIT_MSK (0x007F0000) +#define IWM_SCD_GP_CTRL_ENABLE_31_QUEUES (1 << 0) +#define IWM_SCD_GP_CTRL_AUTO_ACTIVE_MODE (1 << 18) /* Context Data */ #define IWM_SCD_CONTEXT_MEM_LOWER_BOUND (IWM_SCD_MEM_LOWER_BOUND + 0x600) @@ -959,6 +1177,8 @@ struct iwm_tlv_ucode_header { #define IWM_SCD_CHAINEXT_EN (IWM_SCD_BASE + 0x244) #define IWM_SCD_AGGR_SEL (IWM_SCD_BASE + 0x248) #define IWM_SCD_INTERRUPT_MASK (IWM_SCD_BASE + 0x108) +#define IWM_SCD_GP_CTRL (IWM_SCD_BASE + 0x1a8) +#define IWM_SCD_EN_CTRL (IWM_SCD_BASE + 0x254) static inline unsigned int IWM_SCD_QUEUE_WRPTR(unsigned int chnl) { @@ -1460,13 +1680,14 @@ struct iwm_agn_scd_bc_tbl { uint16_t tfd_offset[IWM_TFD_QUEUE_BC_SIZE]; } __packed; -/* maximal number of Tx queues in any platform */ -#define IWM_MVM_MAX_QUEUES 20 +/* Maximum number of Tx queues. */ +#define IWM_MVM_MAX_QUEUES 31 /* Tx queue numbers */ enum { IWM_MVM_OFFCHANNEL_QUEUE = 8, IWM_MVM_CMD_QUEUE = 9, + IWM_MVM_AUX_QUEUE = 15, }; enum iwm_mvm_tx_fifo { @@ -1501,6 +1722,9 @@ enum { IWM_TXPATH_FLUSH = 0x1e, IWM_MGMT_MCAST_KEY = 0x1f, + /* scheduler config */ + IWM_SCD_QUEUE_CFG = 0x1d, + /* global key */ IWM_WEP_KEY = 0x20, @@ -1524,10 +1748,12 @@ enum { /* Scan offload */ IWM_SCAN_OFFLOAD_REQUEST_CMD = 0x51, IWM_SCAN_OFFLOAD_ABORT_CMD = 0x52, - IWM_SCAN_OFFLOAD_COMPLETE = 0x6D, - IWM_SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6E, + IWM_HOT_SPOT_CMD = 0x53, + IWM_SCAN_OFFLOAD_COMPLETE = 0x6d, + IWM_SCAN_OFFLOAD_UPDATE_PROFILES_CMD = 0x6e, IWM_SCAN_OFFLOAD_CONFIG_CMD = 0x6f, IWM_MATCH_FOUND_NOTIFICATION = 0xd9, + IWM_SCAN_ITERATION_COMPLETE = 0xe7, /* Phy */ IWM_PHY_CONFIGURATION_CMD = 0x6a, @@ -1566,6 +1792,8 @@ enum { IWM_MISSED_BEACONS_NOTIFICATION = 0xa2, + IWM_MFUART_LOAD_NOTIFICATION = 0xb1, + /* Power - new power table command */ IWM_MAC_PM_POWER_TABLE = 0xa9, @@ -1573,6 +1801,10 @@ enum { IWM_REPLY_RX_MPDU_CMD = 0xc1, IWM_BA_NOTIF = 0xc5, + /* Location Aware Regulatory */ + IWM_MCC_UPDATE_CMD = 0xc8, + IWM_MCC_CHUB_UPDATE_CMD = 0xc9, + /* BT Coex */ IWM_BT_COEX_PRIO_TABLE = 0xcc, IWM_BT_COEX_PROT_ENV = 0xcd, @@ -1582,6 +1814,10 @@ enum { IWM_REPLY_SF_CFG_CMD = 0xd1, IWM_REPLY_BEACON_FILTERING_CMD = 0xd2, + /* DTS measurements */ + IWM_CMD_DTS_MEASUREMENT_TRIGGER = 0xdc, + IWM_DTS_MEASUREMENT_NOTIFICATION = 0xdd, + IWM_REPLY_DEBUG_CMD = 0xf0, IWM_DEBUG_LOG_MSG = 0xf7, @@ -1750,7 +1986,7 @@ enum { IWM_NVM_SECTION_TYPE_HW = 0, IWM_NVM_SECTION_TYPE_SW, IWM_NVM_SECTION_TYPE_PAPD, - IWM_NVM_SECTION_TYPE_BT, + IWM_NVM_SECTION_TYPE_REGULATORY, IWM_NVM_SECTION_TYPE_CALIBRATION, IWM_NVM_SECTION_TYPE_PRODUCTION, IWM_NVM_SECTION_TYPE_POST_FCS_CALIB, @@ -1824,7 +2060,7 @@ enum { #define IWM_ALIVE_FLG_RFKILL (1 << 0) -struct iwm_mvm_alive_resp { +struct iwm_mvm_alive_resp_v1 { uint16_t status; uint16_t flags; uint8_t ucode_minor; @@ -1846,6 +2082,59 @@ struct iwm_mvm_alive_resp { uint32_t scd_base_ptr; /* SRAM address for SCD */ } __packed; /* IWM_ALIVE_RES_API_S_VER_1 */ +struct iwm_mvm_alive_resp_v2 { + uint16_t status; + uint16_t flags; + uint8_t ucode_minor; + uint8_t ucode_major; + uint16_t id; + uint8_t api_minor; + uint8_t api_major; + uint8_t ver_subtype; + uint8_t ver_type; + uint8_t mac; + uint8_t opt; + uint16_t reserved2; + uint32_t timestamp; + uint32_t error_event_table_ptr; /* SRAM address for error log */ + uint32_t log_event_table_ptr; /* SRAM address for LMAC event log */ + uint32_t cpu_register_ptr; + uint32_t dbgm_config_ptr; + uint32_t alive_counter_ptr; + uint32_t scd_base_ptr; /* SRAM address for SCD */ + uint32_t st_fwrd_addr; /* pointer to Store and forward */ + uint32_t st_fwrd_size; + uint8_t umac_minor; /* UMAC version: minor */ + uint8_t umac_major; /* UMAC version: major */ + uint16_t umac_id; /* UMAC version: id */ + uint32_t error_info_addr; /* SRAM address for UMAC error log */ + uint32_t dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_2 */ + +struct iwm_mvm_alive_resp_v3 { + uint16_t status; + uint16_t flags; + uint32_t ucode_minor; + uint32_t ucode_major; + uint8_t ver_subtype; + uint8_t ver_type; + uint8_t mac; + uint8_t opt; + uint32_t timestamp; + uint32_t error_event_table_ptr; /* SRAM address for error log */ + uint32_t log_event_table_ptr; /* SRAM address for LMAC event log */ + uint32_t cpu_register_ptr; + uint32_t dbgm_config_ptr; + uint32_t alive_counter_ptr; + uint32_t scd_base_ptr; /* SRAM address for SCD */ + uint32_t st_fwrd_addr; /* pointer to Store and forward */ + uint32_t st_fwrd_size; + uint32_t umac_minor; /* UMAC version: minor */ + uint32_t umac_major; /* UMAC version: major */ + uint32_t error_info_addr; /* SRAM address for UMAC error log */ + uint32_t dbg_print_buff_addr; +} __packed; /* ALIVE_RES_API_S_VER_3 */ + /* Error response/notification */ enum { IWM_FW_ERR_UNKNOWN_CMD = 0x0, @@ -2153,6 +2442,7 @@ enum { IWM_TE_V2_NOTIF_HOST_FRAG_END = (1 << 5), IWM_TE_V2_NOTIF_INTERNAL_FRAG_START = (1 << 6), IWM_TE_V2_NOTIF_INTERNAL_FRAG_END = (1 << 7), + IWM_T2_V2_START_IMMEDIATELY = (1 << 11), IWM_TE_V2_NOTIF_MSK = 0xff, @@ -2593,6 +2883,21 @@ struct iwm_missed_beacons_notif { } __packed; /* IWM_MISSED_BEACON_NTFY_API_S_VER_3 */ /** + * struct iwm_mfuart_load_notif - mfuart image version & status + * ( IWM_MFUART_LOAD_NOTIFICATION = 0xb1 ) + * @installed_ver: installed image version + * @external_ver: external image version + * @status: MFUART loading status + * @duration: MFUART loading time +*/ +struct iwm_mfuart_load_notif { + uint32_t installed_ver; + uint32_t external_ver; + uint32_t status; + uint32_t duration; +} __packed; /*MFU_LOADER_NTFY_API_S_VER_1*/ + +/** * struct iwm_set_calib_default_cmd - set default value for calibration. * ( IWM_SET_CALIB_DEFAULT_CMD = 0x8e ) * @calib_index: the calibration to set value for @@ -2869,6 +3174,18 @@ enum iwm_sf_scenario { #define IWM_SF_W_MARK_LEGACY 4096 #define IWM_SF_W_MARK_SCAN 4096 +/* SF Scenarios timers for default configuration (aligned to 32 uSec) */ +#define IWM_SF_SINGLE_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_SINGLE_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_AGG_UNICAST_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_AGG_UNICAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_MCAST_IDLE_TIMER_DEF 160 /* 150 mSec */ +#define IWM_SF_MCAST_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_BA_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_BA_AGING_TIMER_DEF 400 /* 0.4 mSec */ +#define IWM_SF_TX_RE_IDLE_TIMER_DEF 160 /* 150 uSec */ +#define IWM_SF_TX_RE_AGING_TIMER_DEF 400 /* 0.4 mSec */ + /* SF Scenarios timers for FULL_ON state (aligned to 32 uSec) */ #define IWM_SF_SINGLE_UNICAST_IDLE_TIMER 320 /* 300 uSec */ #define IWM_SF_SINGLE_UNICAST_AGING_TIMER 2016 /* 2 mSec */ @@ -2883,6 +3200,8 @@ enum iwm_sf_scenario { #define IWM_SF_LONG_DELAY_AGING_TIMER 1000000 /* 1 Sec */ +#define IWM_SF_CFG_DUMMY_NOTIF_OFF (1 << 16) + /** * Smart Fifo configuration command. * @state: smart fifo state, types listed in iwm_sf_sate. @@ -4366,6 +4685,46 @@ static inline uint32_t iwm_mvm_get_scd_ssn(struct iwm_mvm_tx_resp *tx_resp) tx_resp->frame_count) & 0xfff; } +/** + * struct iwm_scd_txq_cfg_cmd - New txq hw scheduler config command + * @token: + * @sta_id: station id + * @tid: + * @scd_queue: scheduler queue to confiug + * @enable: 1 queue enable, 0 queue disable + * @aggregate: 1 aggregated queue, 0 otherwise + * @tx_fifo: %enum iwm_mvm_tx_fifo + * @window: BA window size + * @ssn: SSN for the BA agreement + */ +struct iwm_scd_txq_cfg_cmd { + uint8_t token; + uint8_t sta_id; + uint8_t tid; + uint8_t scd_queue; + uint8_t enable; + uint8_t aggregate; + uint8_t tx_fifo; + uint8_t window; + uint16_t ssn; + uint16_t reserved; +} __packed; /* SCD_QUEUE_CFG_CMD_API_S_VER_1 */ + +/** + * struct iwm_scd_txq_cfg_rsp + * @token: taken from the command + * @sta_id: station id from the command + * @tid: tid from the command + * @scd_queue: scd_queue from the command + */ +struct iwm_scd_txq_cfg_rsp { + uint8_t token; + uint8_t sta_id; + uint8_t tid; + uint8_t scd_queue; +} __packed; /* SCD_QUEUE_CFG_RSP_API_S_VER_1 */ + + /* Scan Commands, Responses, Notifications */ /* Masks for iwm_scan_channel.type flags */ @@ -4430,6 +4789,23 @@ struct iwm_ssid_ie { uint8_t ssid[IEEE80211_NWID_LEN]; } __packed; /* IWM_SCAN_DIRECT_SSID_IE_API_S_VER_1 */ +/* scan offload */ +#define IWM_MAX_SCAN_CHANNELS 40 +#define IWM_SCAN_MAX_BLACKLIST_LEN 64 +#define IWM_SCAN_SHORT_BLACKLIST_LEN 16 +#define IWM_SCAN_MAX_PROFILES 11 +#define IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE 512 + +/* Default watchdog (in MS) for scheduled scan iteration */ +#define IWM_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) + +#define IWM_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) +#define IWM_CAN_ABORT_STATUS 1 + +#define IWM_FULL_SCAN_MULTIPLIER 5 +#define IWM_FAST_SCHED_SCAN_ITERATIONS 3 +#define IWM_MAX_SCHED_SCAN_PLANS 2 + /** * iwm_scan_flags - masks for scan command flags *@IWM_SCAN_FLAGS_PERIODIC_SCAN: @@ -4473,64 +4849,194 @@ enum iwm_scan_type { #define IWM_MAX_NUM_SCAN_CHANNELS 0x24 /** - * struct iwm_scan_cmd - scan request command - * ( IWM_SCAN_REQUEST_CMD = 0x80 ) - * @len: command length in bytes - * @scan_flags: scan flags from IWM_SCAN_FLAGS_* - * @channel_count: num of channels in channel list (1 - IWM_MAX_NUM_SCAN_CHANNELS) - * @quiet_time: in msecs, dwell this time for active scan on quiet channels - * @quiet_plcp_th: quiet PLCP threshold (channel is quiet if less than - * this number of packets were received (typically 1) - * @passive2active: is auto switching from passive to active during scan allowed - * @rxchain_sel_flags: RXON_RX_CHAIN_* - * @max_out_time: in usecs, max out of serving channel time - * @suspend_time: how long to pause scan when returning to service channel: - * bits 0-19: beacon interal in usecs (suspend before executing) - * bits 20-23: reserved - * bits 24-31: number of beacons (suspend between channels) - * @rxon_flags: RXON_FLG_* - * @filter_flags: RXON_FILTER_* - * @tx_cmd: for active scans (zero for passive), w/o payload, - * no RS so specify TX rate - * @direct_scan: direct scan SSIDs - * @type: one of IWM_SCAN_TYPE_* - * @repeats: how many time to repeat the scan - */ -struct iwm_scan_cmd { + * iwm_scan_schedule_lmac - schedule of scan offload + * @delay: delay between iterations, in seconds. + * @iterations: num of scan iterations + * @full_scan_mul: number of partial scans before each full scan + */ +struct iwm_scan_schedule_lmac { + uint16_t delay; + uint8_t iterations; + uint8_t full_scan_mul; +} __packed; /* SCAN_SCHEDULE_API_S */ + +/** + * iwm_scan_req_tx_cmd - SCAN_REQ_TX_CMD_API_S + * @tx_flags: combination of TX_CMD_FLG_* + * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is + * cleared. Combination of RATE_MCS_* + * @sta_id: index of destination station in FW station table + * @reserved: for alignment and future use + */ +struct iwm_scan_req_tx_cmd { + uint32_t tx_flags; + uint32_t rate_n_flags; + uint8_t sta_id; + uint8_t reserved[3]; +} __packed; + +enum iwm_scan_channel_flags_lmac { + IWM_UNIFIED_SCAN_CHANNEL_FULL = (1 << 27), + IWM_UNIFIED_SCAN_CHANNEL_PARTIAL = (1 << 28), +}; + +/** + * iwm_scan_channel_cfg_lmac - SCAN_CHANNEL_CFG_S_VER2 + * @flags: bits 1-20: directed scan to i'th ssid + * other bits &enum iwm_scan_channel_flags_lmac + * @channel_number: channel number 1-13 etc + * @iter_count: scan iteration on this channel + * @iter_interval: interval in seconds between iterations on one channel + */ +struct iwm_scan_channel_cfg_lmac { + uint32_t flags; + uint16_t channel_num; + uint16_t iter_count; + uint32_t iter_interval; +} __packed; + +/* + * iwm_scan_probe_segment - PROBE_SEGMENT_API_S_VER_1 + * @offset: offset in the data block + * @len: length of the segment + */ +struct iwm_scan_probe_segment { + uint16_t offset; uint16_t len; - uint8_t scan_flags; - uint8_t channel_count; - uint16_t quiet_time; - uint16_t quiet_plcp_th; - uint16_t passive2active; - uint16_t rxchain_sel_flags; +} __packed; + +/* iwm_scan_probe_req - PROBE_REQUEST_FRAME_API_S_VER_2 + * @mac_header: first (and common) part of the probe + * @band_data: band specific data + * @common_data: last (and common) part of the probe + * @buf: raw data block + */ +struct iwm_scan_probe_req { + struct iwm_scan_probe_segment mac_header; + struct iwm_scan_probe_segment band_data[2]; + struct iwm_scan_probe_segment common_data; + uint8_t buf[IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE]; +} __packed; + +enum iwm_scan_channel_flags { + IWM_SCAN_CHANNEL_FLAG_EBS = (1 << 0), + IWM_SCAN_CHANNEL_FLAG_EBS_ACCURATE = (1 << 1), + IWM_SCAN_CHANNEL_FLAG_CACHE_ADD = (1 << 2), +}; + +/* iwm_scan_channel_opt - CHANNEL_OPTIMIZATION_API_S + * @flags: enum iwm_scan_channel_flags + * @non_ebs_ratio: defines the ratio of number of scan iterations where EBS is + * involved. + * 1 - EBS is disabled. + * 2 - every second scan will be full scan(and so on). + */ +struct iwm_scan_channel_opt { + uint16_t flags; + uint16_t non_ebs_ratio; +} __packed; + +/** + * iwm_mvm_lmac_scan_flags + * @IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL: pass all beacons and probe responses + * without filtering. + * @IWM_MVM_LMAC_SCAN_FLAG_PASSIVE: force passive scan on all channels + * @IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION: single channel scan + * @IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE: send iteration complete notification + * @IWM_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS multiple SSID matching + * @IWM_MVM_LMAC_SCAN_FLAG_FRAGMENTED: all passive scans will be fragmented + * @IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED: insert WFA vendor-specific TPC report + * and DS parameter set IEs into probe requests. + * @IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL: use extended dwell time on channels + * 1, 6 and 11. + * @IWM_MVM_LMAC_SCAN_FLAG_MATCH: Send match found notification on matches + */ +enum iwm_mvm_lmac_scan_flags { + IWM_MVM_LMAC_SCAN_FLAG_PASS_ALL = (1 << 0), + IWM_MVM_LMAC_SCAN_FLAG_PASSIVE = (1 << 1), + IWM_MVM_LMAC_SCAN_FLAG_PRE_CONNECTION = (1 << 2), + IWM_MVM_LMAC_SCAN_FLAG_ITER_COMPLETE = (1 << 3), + IWM_MVM_LMAC_SCAN_FLAG_MULTIPLE_SSIDS = (1 << 4), + IWM_MVM_LMAC_SCAN_FLAG_FRAGMENTED = (1 << 5), + IWM_MVM_LMAC_SCAN_FLAGS_RRM_ENABLED = (1 << 6), + IWM_MVM_LMAC_SCAN_FLAG_EXTENDED_DWELL = (1 << 7), + IWM_MVM_LMAC_SCAN_FLAG_MATCH = (1 << 9), +}; + +enum iwm_scan_priority { + IWM_SCAN_PRIORITY_LOW, + IWM_SCAN_PRIORITY_MEDIUM, + IWM_SCAN_PRIORITY_HIGH, +}; + +/** + * iwm_scan_req_lmac - SCAN_REQUEST_CMD_API_S_VER_1 + * @reserved1: for alignment and future use + * @channel_num: num of channels to scan + * @active-dwell: dwell time for active channels + * @passive-dwell: dwell time for passive channels + * @fragmented-dwell: dwell time for fragmented passive scan + * @extended_dwell: dwell time for channels 1, 6 and 11 (in certain cases) + * @reserved2: for alignment and future use + * @rx_chain_selct: PHY_RX_CHAIN_* flags + * @scan_flags: &enum iwm_mvm_lmac_scan_flags + * @max_out_time: max time (in TU) to be out of associated channel + * @suspend_time: pause scan this long (TUs) when returning to service channel + * @flags: RXON flags + * @filter_flags: RXON filter + * @tx_cmd: tx command for active scan; for 2GHz and for 5GHz + * @direct_scan: list of SSIDs for directed active scan + * @scan_prio: enum iwm_scan_priority + * @iter_num: number of scan iterations + * @delay: delay in seconds before first iteration + * @schedule: two scheduling plans. The first one is finite, the second one can + * be infinite. + * @channel_opt: channel optimization options, for full and partial scan + * @data: channel configuration and probe request packet. + */ +struct iwm_scan_req_lmac { + /* SCAN_REQUEST_FIXED_PART_API_S_VER_7 */ + uint32_t reserved1; + uint8_t n_channels; + uint8_t active_dwell; + uint8_t passive_dwell; + uint8_t fragmented_dwell; + uint8_t extended_dwell; + uint8_t reserved2; + uint16_t rx_chain_select; + uint32_t scan_flags; uint32_t max_out_time; uint32_t suspend_time; - /* IWM_RX_ON_FLAGS_API_S_VER_1 */ - uint32_t rxon_flags; + /* RX_ON_FLAGS_API_S_VER_1 */ + uint32_t flags; uint32_t filter_flags; - struct iwm_tx_cmd tx_cmd; + struct iwm_scan_req_tx_cmd tx_cmd[2]; struct iwm_ssid_ie direct_scan[IWM_PROBE_OPTION_MAX]; - uint32_t type; - uint32_t repeats; + uint32_t scan_prio; + /* SCAN_REQ_PERIODIC_PARAMS_API_S */ + uint32_t iter_num; + uint32_t delay; + struct iwm_scan_schedule_lmac schedule[IWM_MAX_SCHED_SCAN_PLANS]; + struct iwm_scan_channel_opt channel_opt[2]; + uint8_t data[]; +} __packed; - /* - * Probe request frame, followed by channel list. - * - * Size of probe request frame is specified by byte count in tx_cmd. - * Channel list follows immediately after probe request frame. - * Number of channels in list is specified by channel_count. - * Each channel in list is of type: - * - * struct iwm_scan_channel channels[0]; - * - * NOTE: Only one band of channels can be scanned per pass. You - * must not mix 2.4GHz channels and 5.2GHz channels, and you must wait - * for one scan to complete (i.e. receive IWM_SCAN_COMPLETE_NOTIFICATION) - * before requesting another scan. - */ - uint8_t data[0]; -} __packed; /* IWM_SCAN_REQUEST_FIXED_PART_API_S_VER_5 */ +/** + * iwm_scan_offload_complete - PERIODIC_SCAN_COMPLETE_NTF_API_S_VER_2 + * @last_schedule_line: last schedule line executed (fast or regular) + * @last_schedule_iteration: last scan iteration executed before scan abort + * @status: enum iwm_scan_offload_complete_status + * @ebs_status: EBS success status &enum iwm_scan_ebs_status + * @time_after_last_iter; time in seconds elapsed after last iteration + */ +struct iwm_periodic_scan_complete { + uint8_t last_schedule_line; + uint8_t last_schedule_iteration; + uint8_t status; + uint8_t ebs_status; + uint32_t time_after_last_iter; + uint32_t reserved; +} __packed; /* Response to scan request contains only status with one of these values */ #define IWM_SCAN_RESPONSE_OK 0x1 @@ -4653,22 +5159,6 @@ struct iwm_scan_complete_notif { struct iwm_scan_results_notif results[IWM_MAX_NUM_SCAN_CHANNELS]; } __packed; /* IWM_SCAN_COMPLETE_NTF_API_S_VER_2 */ -/* scan offload */ -#define IWM_MAX_SCAN_CHANNELS 40 -#define IWM_SCAN_MAX_BLACKLIST_LEN 64 -#define IWM_SCAN_SHORT_BLACKLIST_LEN 16 -#define IWM_SCAN_MAX_PROFILES 11 -#define IWM_SCAN_OFFLOAD_PROBE_REQ_SIZE 512 - -/* Default watchdog (in MS) for scheduled scan iteration */ -#define IWM_SCHED_SCAN_WATCHDOG cpu_to_le16(15000) - -#define IWM_GOOD_CRC_TH_DEFAULT cpu_to_le16(1) -#define IWM_CAN_ABORT_STATUS 1 - -#define IWM_FULL_SCAN_MULTIPLIER 5 -#define IWM_FAST_SCHED_SCAN_ITERATIONS 3 - enum iwm_scan_framework_client { IWM_SCAN_CLIENT_SCHED_SCAN = (1 << 0), IWM_SCAN_CLIENT_NETDETECT = (1 << 1), @@ -4864,6 +5354,28 @@ enum iwm_scan_offload_compleate_status { }; /** + * struct iwm_lmac_scan_complete_notif - notifies end of scanning (all channels) + * SCAN_COMPLETE_NTF_API_S_VER_3 + * @scanned_channels: number of channels scanned (and number of valid results) + * @status: one of SCAN_COMP_STATUS_* + * @bt_status: BT on/off status + * @last_channel: last channel that was scanned + * @tsf_low: TSF timer (lower half) in usecs + * @tsf_high: TSF timer (higher half) in usecs + * @results: an array of scan results, only "scanned_channels" of them are valid + */ +struct iwm_lmac_scan_complete_notif { + uint8_t scanned_channels; + uint8_t status; + uint8_t bt_status; + uint8_t last_channel; + uint32_t tsf_low; + uint32_t tsf_high; + struct iwm_scan_results_notif results[]; +} __packed; + + +/** * iwm_scan_offload_complete - IWM_SCAN_OFFLOAD_COMPLETE_NTF_API_S_VER_1 * @last_schedule_line: last schedule line executed (fast or regular) * @last_schedule_iteration: last scan iteration executed before scan abort @@ -4887,11 +5399,13 @@ struct iwm_sched_scan_results { uint8_t reserved; }; +/* STA API */ + /** * enum iwm_sta_flags - flags for the ADD_STA host command * @IWM_STA_FLG_REDUCED_TX_PWR_CTRL: * @IWM_STA_FLG_REDUCED_TX_PWR_DATA: - * @IWM_STA_FLG_FLG_ANT_MSK: Antenna selection + * @IWM_STA_FLG_DISABLE_TX: set if TX should be disabled * @IWM_STA_FLG_PS: set if STA is in Power Save * @IWM_STA_FLG_INVALID: set if STA is invalid * @IWM_STA_FLG_DLP_EN: Direct Link Protocol is enabled @@ -4915,10 +5429,7 @@ enum iwm_sta_flags { IWM_STA_FLG_REDUCED_TX_PWR_CTRL = (1 << 3), IWM_STA_FLG_REDUCED_TX_PWR_DATA = (1 << 6), - IWM_STA_FLG_FLG_ANT_A = (1 << 4), - IWM_STA_FLG_FLG_ANT_B = (2 << 4), - IWM_STA_FLG_FLG_ANT_MSK = (IWM_STA_FLG_FLG_ANT_A | - IWM_STA_FLG_FLG_ANT_B), + IWM_STA_FLG_DISABLE_TX = (1 << 4), IWM_STA_FLG_PS = (1 << 8), IWM_STA_FLG_DRAIN_FLOW = (1 << 12), @@ -4996,7 +5507,7 @@ enum iwm_sta_key_flag { /** * enum iwm_sta_modify_flag - indicate to the fw what flag are being changed - * @IWM_STA_MODIFY_KEY: this command modifies %key + * @IWM_STA_MODIFY_QUEUE_REMOVAL: this command removes a queue * @IWM_STA_MODIFY_TID_DISABLE_TX: this command modifies %tid_disable_tx * @IWM_STA_MODIFY_TX_RATE: unused * @IWM_STA_MODIFY_ADD_BA_TID: this command modifies %add_immediate_ba_tid @@ -5006,7 +5517,7 @@ enum iwm_sta_key_flag { * @IWM_STA_MODIFY_QUEUES: modify the queues used by this station */ enum iwm_sta_modify_flag { - IWM_STA_MODIFY_KEY = (1 << 0), + IWM_STA_MODIFY_QUEUE_REMOVAL = (1 << 0), IWM_STA_MODIFY_TID_DISABLE_TX = (1 << 1), IWM_STA_MODIFY_TX_RATE = (1 << 2), IWM_STA_MODIFY_ADD_BA_TID = (1 << 3), @@ -5023,11 +5534,14 @@ enum iwm_sta_modify_flag { * @IWM_STA_SLEEP_STATE_AWAKE: * @IWM_STA_SLEEP_STATE_PS_POLL: * @IWM_STA_SLEEP_STATE_UAPSD: + * @IWM_STA_SLEEP_STATE_MOREDATA: set more-data bit on + * (last) released frame */ enum iwm_sta_sleep_flag { IWM_STA_SLEEP_STATE_AWAKE = 0, IWM_STA_SLEEP_STATE_PS_POLL = (1 << 0), IWM_STA_SLEEP_STATE_UAPSD = (1 << 1), + IWM_STA_SLEEP_STATE_MOREDATA = (1 << 2), }; /* STA ID and color bits definitions */ @@ -5075,23 +5589,25 @@ struct iwm_mvm_keyinfo { uint64_t hw_tkip_mic_tx_key; } __packed; +#define IWM_ADD_STA_STATUS_MASK 0xFF +#define IWM_ADD_STA_BAID_VALID_MASK 0x8000 +#define IWM_ADD_STA_BAID_MASK 0x7F00 +#define IWM_ADD_STA_BAID_SHIFT 8 + /** - * struct iwm_mvm_add_sta_cmd_v5 - Add/modify a station in the fw's sta table. - * ( IWM_REPLY_ADD_STA = 0x18 ) + * struct iwm_mvm_add_sta_cmd_v7 - Add/modify a station in the fw's sta table. + * ( REPLY_ADD_STA = 0x18 ) * @add_modify: 1: modify existing, 0: add new station - * @unicast_tx_key_id: unicast tx key id. Relevant only when unicast key sent - * @multicast_tx_key_id: multicast tx key id. Relevant only when multicast key - * sent + * @awake_acs: + * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable + * AMPDU for tid x. Set %IWM_STA_MODIFY_TID_DISABLE_TX to change this field. * @mac_id_n_color: the Mac context this station belongs to * @addr[ETHER_ADDR_LEN]: station's MAC address * @sta_id: index of station in uCode's station table * @modify_mask: IWM_STA_MODIFY_*, selects which parameters to modify vs. leave * alone. 1 - modify, 0 - don't change. - * @key: look at %iwm_mvm_keyinfo * @station_flags: look at %iwm_sta_flags * @station_flags_msk: what of %station_flags have changed - * @tid_disable_tx: is tid BIT(tid) enabled for Tx. Clear BIT(x) to enable - * AMPDU for tid x. Set %IWM_STA_MODIFY_TID_DISABLE_TX to change this field. * @add_immediate_ba_tid: tid for which to add block-ack support (Rx) * Set %IWM_STA_MODIFY_ADD_BA_TID to use this field, and also set * add_immediate_ba_ssn. @@ -5115,40 +5631,9 @@ struct iwm_mvm_keyinfo { * ADD_STA sets up the table entry for one station, either creating a new * entry, or modifying a pre-existing one. */ -struct iwm_mvm_add_sta_cmd_v5 { - uint8_t add_modify; - uint8_t unicast_tx_key_id; - uint8_t multicast_tx_key_id; - uint8_t reserved1; - uint32_t mac_id_n_color; - uint8_t addr[ETHER_ADDR_LEN]; - uint16_t reserved2; - uint8_t sta_id; - uint8_t modify_mask; - uint16_t reserved3; - struct iwm_mvm_keyinfo key; - uint32_t station_flags; - uint32_t station_flags_msk; - uint16_t tid_disable_tx; - uint16_t reserved4; - uint8_t add_immediate_ba_tid; - uint8_t remove_immediate_ba_tid; - uint16_t add_immediate_ba_ssn; - uint16_t sleep_tx_count; - uint16_t sleep_state_flags; - uint16_t assoc_id; - uint16_t beamform_flags; - uint32_t tfd_queue_msk; -} __packed; /* IWM_ADD_STA_CMD_API_S_VER_5 */ - -/** - * struct iwm_mvm_add_sta_cmd_v6 - Add / modify a station - * VER_6 of this command is quite similar to VER_5 except - * exclusion of all fields related to the security key installation. - */ -struct iwm_mvm_add_sta_cmd_v6 { +struct iwm_mvm_add_sta_cmd_v7 { uint8_t add_modify; - uint8_t reserved1; + uint8_t awake_acs; uint16_t tid_disable_tx; uint32_t mac_id_n_color; uint8_t addr[ETHER_ADDR_LEN]; /* _STA_ID_MODIFY_INFO_API_S_VER_1 */ @@ -5166,7 +5651,7 @@ struct iwm_mvm_add_sta_cmd_v6 { uint16_t assoc_id; uint16_t beamform_flags; uint32_t tfd_queue_msk; -} __packed; /* IWM_ADD_STA_CMD_API_S_VER_6 */ +} __packed; /* ADD_STA_CMD_API_S_VER_7 */ /** * struct iwm_mvm_add_sta_key_cmd - add/modify sta key @@ -5256,6 +5741,175 @@ struct iwm_mvm_wep_key_cmd { struct iwm_mvm_wep_key wep_key[0]; } __packed; /* SEC_CURR_WEP_KEY_CMD_API_S_VER_2 */ +/* + * BT coex + */ + +enum iwm_bt_coex_mode { + IWM_BT_COEX_DISABLE = 0x0, + IWM_BT_COEX_NW = 0x1, + IWM_BT_COEX_BT = 0x2, + IWM_BT_COEX_WIFI = 0x3, +}; /* BT_COEX_MODES_E */ + +enum iwm_bt_coex_enabled_modules { + IWM_BT_COEX_MPLUT_ENABLED = (1 << 0), + IWM_BT_COEX_MPLUT_BOOST_ENABLED = (1 << 1), + IWM_BT_COEX_SYNC2SCO_ENABLED = (1 << 2), + IWM_BT_COEX_CORUN_ENABLED = (1 << 3), + IWM_BT_COEX_HIGH_BAND_RET = (1 << 4), +}; /* BT_COEX_MODULES_ENABLE_E_VER_1 */ + +/** + * struct iwm_bt_coex_cmd - bt coex configuration command + * @mode: enum %iwm_bt_coex_mode + * @enabled_modules: enum %iwm_bt_coex_enabled_modules + * + * The structure is used for the BT_COEX command. + */ +struct iwm_bt_coex_cmd { + uint32_t mode; + uint32_t enabled_modules; +} __packed; /* BT_COEX_CMD_API_S_VER_6 */ + + +/* + * Location Aware Regulatory (LAR) API - MCC updates + */ + +/** + * struct iwm_mcc_update_cmd_v1 - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwm_mcc_source + * @reserved: reserved for alignment + */ +struct iwm_mcc_update_cmd_v1 { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_1 */ + +/** + * struct iwm_mcc_update_cmd - Request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: the source from where we got the MCC, see iwm_mcc_source + * @reserved: reserved for alignment + * @key: integrity key for MCC API OEM testing + * @reserved2: reserved + */ +struct iwm_mcc_update_cmd { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved; + uint32_t key; + uint32_t reserved2[5]; +} __packed; /* LAR_UPDATE_MCC_CMD_API_S_VER_2 */ + +/** + * iwm_mcc_update_resp_v1 - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwm_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwm_mcc_source + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwm_mcc_update_resp_v1 { + uint32_t status; + uint16_t mcc; + uint8_t cap; + uint8_t source_id; + uint32_t n_channels; + uint32_t channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_1 */ + +/** + * iwm_mcc_update_resp - response to MCC_UPDATE_CMD. + * Contains the new channel control profile map, if changed, and the new MCC + * (mobile country code). + * The new MCC may be different than what was requested in MCC_UPDATE_CMD. + * @status: see &enum iwm_mcc_update_status + * @mcc: the new applied MCC + * @cap: capabilities for all channels which matches the MCC + * @source_id: the MCC source, see iwm_mcc_source + * @time: time elapsed from the MCC test start (in 30 seconds TU) + * @reserved: reserved. + * @n_channels: number of channels in @channels_data (may be 14, 39, 50 or 51 + * channels, depending on platform) + * @channels: channel control data map, DWORD for each channel. Only the first + * 16bits are used. + */ +struct iwm_mcc_update_resp { + uint32_t status; + uint16_t mcc; + uint8_t cap; + uint8_t source_id; + uint16_t time; + uint16_t reserved; + uint32_t n_channels; + uint32_t channels[0]; +} __packed; /* LAR_UPDATE_MCC_CMD_RESP_S_VER_2 */ + +/** + * struct iwm_mcc_chub_notif - chub notifies of mcc change + * (MCC_CHUB_UPDATE_CMD = 0xc9) + * The Chub (Communication Hub, CommsHUB) is a HW component that connects to + * the cellular and connectivity cores that gets updates of the mcc, and + * notifies the ucode directly of any mcc change. + * The ucode requests the driver to request the device to update geographic + * regulatory profile according to the given MCC (Mobile Country Code). + * The MCC is two letter-code, ascii upper case[A-Z] or '00' for world domain. + * 'ZZ' MCC will be used to switch to NVM default profile; in this case, the + * MCC in the cmd response will be the relevant MCC in the NVM. + * @mcc: given mobile country code + * @source_id: identity of the change originator, see iwm_mcc_source + * @reserved1: reserved for alignment + */ +struct iwm_mcc_chub_notif { + uint16_t mcc; + uint8_t source_id; + uint8_t reserved1; +} __packed; /* LAR_MCC_NOTIFY_S */ + +enum iwm_mcc_update_status { + IWM_MCC_RESP_NEW_CHAN_PROFILE, + IWM_MCC_RESP_SAME_CHAN_PROFILE, + IWM_MCC_RESP_INVALID, + IWM_MCC_RESP_NVM_DISABLED, + IWM_MCC_RESP_ILLEGAL, + IWM_MCC_RESP_LOW_PRIORITY, + IWM_MCC_RESP_TEST_MODE_ACTIVE, + IWM_MCC_RESP_TEST_MODE_NOT_ACTIVE, + IWM_MCC_RESP_TEST_MODE_DENIAL_OF_SERVICE, +}; + +enum iwm_mcc_source { + IWM_MCC_SOURCE_OLD_FW = 0, + IWM_MCC_SOURCE_ME = 1, + IWM_MCC_SOURCE_BIOS = 2, + IWM_MCC_SOURCE_3G_LTE_HOST = 3, + IWM_MCC_SOURCE_3G_LTE_DEVICE = 4, + IWM_MCC_SOURCE_WIFI = 5, + IWM_MCC_SOURCE_RESERVED = 6, + IWM_MCC_SOURCE_DEFAULT = 7, + IWM_MCC_SOURCE_UNINITIALIZED = 8, + IWM_MCC_SOURCE_MCC_API = 9, + IWM_MCC_SOURCE_GET_CURRENT = 0x10, + IWM_MCC_SOURCE_GETTING_MCC_TEST_MODE = 0x11, +}; /* * Some cherry-picked definitions @@ -5263,6 +5917,48 @@ struct iwm_mvm_wep_key_cmd { #define IWM_FRAME_LIMIT 64 +/* + * From Linux commit ab02165ccec4c78162501acedeef1a768acdb811: + * As the firmware is slowly running out of command IDs and grouping of + * commands is desirable anyway, the firmware is extending the command + * header from 4 bytes to 8 bytes to introduce a group (in place of the + * former flags field, since that's always 0 on commands and thus can + * be easily used to distinguish between the two). + * + * These functions retrieve specific information from the id field in + * the iwm_host_cmd struct which contains the command id, the group id, + * and the version of the command. +*/ +static inline uint8_t +iwm_cmd_opcode(uint32_t cmdid) +{ + return cmdid & 0xff; +} + +static inline uint8_t +iwm_cmd_groupid(uint32_t cmdid) +{ + return ((cmdid & 0Xff00) >> 8); +} + +static inline uint8_t +iwm_cmd_version(uint32_t cmdid) +{ + return ((cmdid & 0xff0000) >> 16); +} + +static inline uint32_t +iwm_cmd_id(uint8_t opcode, uint8_t groupid, uint8_t version) +{ + return opcode + (groupid << 8) + (version << 16); +} + +/* make uint16_t wide id out of uint8_t group and opcode */ +#define IWM_WIDE_ID(grp, opcode) ((grp << 8) | opcode) + +/* due to the conversion, this group is special */ +#define IWM_ALWAYS_LONG_GROUP 1 + struct iwm_cmd_header { uint8_t code; uint8_t flags; @@ -5270,6 +5966,16 @@ struct iwm_cmd_header { uint8_t qid; } __packed; +struct iwm_cmd_header_wide { + uint8_t opcode; + uint8_t group_id; + uint8_t idx; + uint8_t qid; + uint16_t length; + uint8_t reserved; + uint8_t version; +} __packed; + enum iwm_power_scheme { IWM_POWER_SCHEME_CAM = 1, IWM_POWER_SCHEME_BPS, @@ -5280,10 +5986,26 @@ enum iwm_power_scheme { #define IWM_MAX_CMD_PAYLOAD_SIZE ((4096 - 4) - sizeof(struct iwm_cmd_header)) #define IWM_CMD_FAILED_MSK 0x40 +/** + * struct iwm_device_cmd + * + * For allocation of the command and tx queues, this establishes the overall + * size of the largest command we send to uCode, except for commands that + * aren't fully copied and use other TFD space. + */ struct iwm_device_cmd { - struct iwm_cmd_header hdr; - - uint8_t data[IWM_DEF_CMD_PAYLOAD_SIZE]; + union { + struct { + struct iwm_cmd_header hdr; + uint8_t data[IWM_DEF_CMD_PAYLOAD_SIZE]; + }; + struct { + struct iwm_cmd_header_wide hdr_wide; + uint8_t data_wide[IWM_DEF_CMD_PAYLOAD_SIZE - + sizeof(struct iwm_cmd_header_wide) + + sizeof(struct iwm_cmd_header)]; + }; + }; } __packed; struct iwm_rx_packet { diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h index 95688e67c8f..c8efd2cdc84 100644 --- a/sys/dev/pci/if_iwmvar.h +++ b/sys/dev/pci/if_iwmvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmvar.h,v 1.16 2016/01/25 11:27:11 stsp Exp $ */ +/* $OpenBSD: if_iwmvar.h,v 1.17 2016/05/25 13:35:12 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh <info@genua.de> @@ -137,7 +137,7 @@ struct iwm_tx_radiotap_header { (1 << IEEE80211_RADIOTAP_CHANNEL) | \ (1 << IEEE80211_RADIOTAP_HWQUEUE)) -#define IWM_UCODE_SECT_MAX 6 +#define IWM_UCODE_SECT_MAX 16 #define IWM_FWDMASEGSZ (192*1024) /* sanity check value */ #define IWM_FWMAXSIZE (2*1024*1024) @@ -152,9 +152,10 @@ struct iwm_tx_radiotap_header { #define IWM_FW_STATUS_DONE 2 enum iwm_ucode_type { - IWM_UCODE_TYPE_INIT, IWM_UCODE_TYPE_REGULAR, + IWM_UCODE_TYPE_INIT, IWM_UCODE_TYPE_WOW, + IWM_UCODE_TYPE_REGULAR_USNIFFER, IWM_UCODE_TYPE_MAX }; @@ -217,7 +218,7 @@ struct iwm_host_cmd { uint32_t flags; uint16_t len[IWM_MAX_CMD_TBS_PER_TFD]; uint8_t dataflags[IWM_MAX_CMD_TBS_PER_TFD]; - uint8_t id; + uint32_t id; }; /* @@ -290,6 +291,7 @@ struct iwm_rx_ring { #define IWM_FLAG_STOPPED 0x04 #define IWM_FLAG_RFKILL 0x08 #define IWM_FLAG_BUSY 0x10 +#define IWM_FLAG_SCANNING 0x20 struct iwm_ucode_status { uint32_t uc_error_event_table; @@ -304,7 +306,8 @@ struct iwm_ucode_status { #define IWM_CMD_RESP_MAX PAGE_SIZE -#define IWM_OTP_LOW_IMAGE_SIZE 2048 +/* lower blocks contain EEPROM image and calibration data */ +#define IWM_OTP_LOW_IMAGE_SIZE_FAMILY_7000 16384 #define IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 1000 #define IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400 @@ -320,7 +323,7 @@ enum iwm_hcmd_dataflag { IWM_HCMD_DFL_DUP = (1 << 1), }; -#define IWM_NUM_PAPD_CH_GROUPS 4 +#define IWM_NUM_PAPD_CH_GROUPS 9 #define IWM_NUM_TXP_CH_GROUPS 9 struct iwm_phy_db_entry { @@ -413,10 +416,14 @@ struct iwm_softc { struct iwm_ucode_status sc_uc; enum iwm_ucode_type sc_uc_current; - int sc_fwver; + char sc_fwver[32]; int sc_capaflags; int sc_capa_max_probe_len; + int sc_capa_n_scan_channels; + uint32_t sc_ucode_api; + uint8_t sc_enabled_capa[howmany(IWM_NUM_UCODE_TLV_CAPA, NBBY)]; + char sc_fw_mcc[3]; int sc_intmask; int sc_flags; @@ -448,10 +455,7 @@ struct iwm_softc { int sc_tx_timer; int sc_rx_ba_sessions; - struct iwm_scan_cmd *sc_scan_cmd; - size_t sc_scan_cmd_len; int sc_scan_last_antenna; - int sc_scanband; int sc_fixed_ridx; @@ -511,6 +515,7 @@ struct iwm_node { struct ieee80211_amrr_node in_amn; }; #define IWM_STATION_ID 0 +#define IWM_AUX_STA_ID 1 #define IWM_ICT_SIZE 4096 #define IWM_ICT_COUNT (IWM_ICT_SIZE / sizeof (uint32_t)) |