diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm.c index 86974e325c..6771e7dc21 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm.c @@ -245,7 +245,7 @@ static int iwm_firmware_store_section(struct iwm_softc *, const uint8_t *, size_t); static int iwm_set_default_calib(struct iwm_softc *, const void *); static void iwm_fw_info_free(struct iwm_fw_info *); -static int iwm_read_firmware(struct iwm_softc *, enum iwm_ucode_type); +static int iwm_read_firmware(struct iwm_softc *); static int iwm_alloc_fwmem(struct iwm_softc *); static int iwm_alloc_sched(struct iwm_softc *); static int iwm_alloc_kw(struct iwm_softc *); @@ -305,21 +305,22 @@ static int iwm_pcie_load_section(struct iwm_softc *, uint8_t, static int iwm_pcie_load_firmware_chunk(struct iwm_softc *, uint32_t, bus_addr_t, uint32_t); static int iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, - const struct iwm_fw_sects *, + const struct iwm_fw_img *, int, int *); static int iwm_pcie_load_cpu_sections(struct iwm_softc *, - const struct iwm_fw_sects *, + const struct iwm_fw_img *, int, int *); static int iwm_pcie_load_given_ucode_8000(struct iwm_softc *, - const struct iwm_fw_sects *); + const struct iwm_fw_img *); static int iwm_pcie_load_given_ucode(struct iwm_softc *, - const struct iwm_fw_sects *); -static int iwm_start_fw(struct iwm_softc *, const struct iwm_fw_sects *); + const struct iwm_fw_img *); +static int iwm_start_fw(struct iwm_softc *, const struct iwm_fw_img *); static int iwm_send_tx_ant_cfg(struct iwm_softc *, uint8_t); static int iwm_send_phy_cfg_cmd(struct iwm_softc *); static int iwm_mvm_load_ucode_wait_alive(struct iwm_softc *, enum iwm_ucode_type); static int iwm_run_init_mvm_ucode(struct iwm_softc *, int); +static int iwm_mvm_config_ltr(struct iwm_softc *sc); static int iwm_rx_addbuf(struct iwm_softc *, int, int); static int iwm_mvm_get_signal_strength(struct iwm_softc *, struct iwm_rx_phy_info *); @@ -349,11 +350,12 @@ static int iwm_raw_xmit(struct ieee80211_node *, struct mbuf *, const struct ieee80211_bpf_params *); static int iwm_mvm_update_quotas(struct iwm_softc *, struct iwm_vap *); static int iwm_auth(struct ieee80211vap *, struct iwm_softc *); -static int iwm_release(struct iwm_softc *, struct iwm_node *); static struct ieee80211_node * iwm_node_alloc(struct ieee80211vap *, const uint8_t[IEEE80211_ADDR_LEN]); -static void iwm_setrates(struct iwm_softc *, struct iwm_node *); +static uint8_t iwm_rate_from_ucode_rate(uint32_t); +static int iwm_rate2ridx(struct iwm_softc *, uint8_t); +static void iwm_setrates(struct iwm_softc *, struct iwm_node *, int); static int iwm_media_change(struct ifnet *); static int iwm_newstate(struct ieee80211vap *, enum ieee80211_state, int); static void iwm_endscan_cb(void *, int); @@ -424,7 +426,7 @@ static int iwm_firmware_store_section(struct iwm_softc *sc, enum iwm_ucode_type type, const uint8_t *data, size_t dlen) { - struct iwm_fw_sects *fws; + struct iwm_fw_img *fws; struct iwm_fw_desc *fwone; if (type >= IWM_UCODE_TYPE_MAX) @@ -432,11 +434,11 @@ iwm_firmware_store_section(struct iwm_softc *sc, if (dlen < sizeof(uint32_t)) return EINVAL; - fws = &sc->sc_fw.fw_sects[type]; + fws = &sc->sc_fw.img[type]; if (fws->fw_count >= IWM_UCODE_SECTION_MAX) return EINVAL; - fwone = &fws->fw_sect[fws->fw_count]; + fwone = &fws->sec[fws->fw_count]; /* first 32bit are device load offset */ memcpy(&fwone->offset, data, sizeof(uint32_t)); @@ -534,17 +536,16 @@ iwm_fw_info_free(struct iwm_fw_info *fw) { firmware_put(fw->fw_fp, FIRMWARE_UNLOAD); fw->fw_fp = NULL; - /* don't touch fw->fw_status */ - memset(fw->fw_sects, 0, sizeof(fw->fw_sects)); + memset(fw->img, 0, sizeof(fw->img)); } static int -iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) +iwm_read_firmware(struct iwm_softc *sc) { struct iwm_fw_info *fw = &sc->sc_fw; const struct iwm_tlv_ucode_header *uhdr; const struct iwm_ucode_tlv *tlv; - struct iwm_ucode_capabilities *capa = &sc->ucode_capa; + struct iwm_ucode_capabilities *capa = &sc->sc_fw.ucode_capa; enum iwm_ucode_tlv_type tlv_type; const struct firmware *fwp; const uint8_t *data; @@ -556,24 +557,11 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) int error = 0; size_t len; - if (fw->fw_status == IWM_FW_STATUS_DONE && - ucode_type != IWM_UCODE_INIT) - return 0; - - while (fw->fw_status == IWM_FW_STATUS_INPROGRESS) - msleep(&sc->sc_fw, &sc->sc_mtx, 0, "iwmfwp", 0); - fw->fw_status = IWM_FW_STATUS_INPROGRESS; - - if (fw->fw_fp != NULL) - iwm_fw_info_free(fw); - /* * Load firmware into driver memory. * fw_fp will be set. */ - IWM_UNLOCK(sc); fwp = firmware_get(sc->cfg->fw_name); - IWM_LOCK(sc); if (fwp == NULL) { device_printf(sc->sc_dev, "could not read firmware %s (error %d)\n", @@ -632,9 +620,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) case IWM_UCODE_TLV_PROBE_MAX_LEN: if (tlv_len != sizeof(uint32_t)) { device_printf(sc->sc_dev, - "%s: PROBE_MAX_LEN (%d) != sizeof(uint32_t)\n", - __func__, - (int) tlv_len); + "%s: PROBE_MAX_LEN (%u) != sizeof(uint32_t)\n", + __func__, tlv_len); error = EINVAL; goto parse_out; } @@ -653,9 +640,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) case IWM_UCODE_TLV_PAN: if (tlv_len) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_PAN: tlv_len (%d) > 0\n", - __func__, - (int) tlv_len); + "%s: IWM_UCODE_TLV_PAN: tlv_len (%u) > 0\n", + __func__, tlv_len); error = EINVAL; goto parse_out; } @@ -664,17 +650,15 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) case IWM_UCODE_TLV_FLAGS: if (tlv_len < sizeof(uint32_t)) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) < sizeof(uint32_t)\n", - __func__, - (int) tlv_len); + "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%u) < sizeof(uint32_t)\n", + __func__, tlv_len); error = EINVAL; goto parse_out; } if (tlv_len % sizeof(uint32_t)) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%d) %% sizeof(uint32_t)\n", - __func__, - (int) tlv_len); + "%s: IWM_UCODE_TLV_FLAGS: tlv_len (%u) %% sizeof(uint32_t)\n", + __func__, tlv_len); error = EINVAL; goto parse_out; } @@ -696,27 +680,25 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: iwm_store_cscheme(): returned %d\n", - __func__, - error); + __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_NUM_OF_CPU: if (tlv_len != sizeof(uint32_t)) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%d) != sizeof(uint32_t)\n", - __func__, - (int) tlv_len); + "%s: IWM_UCODE_TLV_NUM_OF_CPU: tlv_len (%u) != sizeof(uint32_t)\n", + __func__, tlv_len); error = EINVAL; goto parse_out; } num_of_cpus = le32_to_cpup((const uint32_t *)tlv_data); if (num_of_cpus == 2) { - fw->fw_sects[IWM_UCODE_REGULAR].is_dual_cpus = + fw->img[IWM_UCODE_REGULAR].is_dual_cpus = TRUE; - fw->fw_sects[IWM_UCODE_INIT].is_dual_cpus = + fw->img[IWM_UCODE_INIT].is_dual_cpus = TRUE; - fw->fw_sects[IWM_UCODE_WOWLAN].is_dual_cpus = + fw->img[IWM_UCODE_WOWLAN].is_dual_cpus = TRUE; } else if ((num_of_cpus > 2) || (num_of_cpus < 1)) { device_printf(sc->sc_dev, @@ -731,8 +713,7 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) IWM_UCODE_REGULAR, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_REGULAR: iwm_firmware_store_section() failed; %d\n", - __func__, - error); + __func__, error); goto parse_out; } break; @@ -741,8 +722,7 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) IWM_UCODE_INIT, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_INIT: iwm_firmware_store_section() failed; %d\n", - __func__, - error); + __func__, error); goto parse_out; } break; @@ -751,26 +731,23 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) IWM_UCODE_WOWLAN, tlv_data, tlv_len)) != 0) { device_printf(sc->sc_dev, "%s: IWM_UCODE_WOWLAN: iwm_firmware_store_section() failed; %d\n", - __func__, - error); + __func__, error); goto parse_out; } break; case IWM_UCODE_TLV_DEF_CALIB: if (tlv_len != sizeof(struct iwm_tlv_calib_data)) { device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%d) < sizeof(iwm_tlv_calib_data) (%d)\n", - __func__, - (int) tlv_len, - (int) sizeof(struct iwm_tlv_calib_data)); + "%s: IWM_UCODE_TLV_DEV_CALIB: tlv_len (%u) < sizeof(iwm_tlv_calib_data) (%zu)\n", + __func__, tlv_len, + sizeof(struct iwm_tlv_calib_data)); error = EINVAL; goto parse_out; } if ((error = iwm_set_default_calib(sc, tlv_data)) != 0) { device_printf(sc->sc_dev, "%s: iwm_set_default_calib() failed: %d\n", - __func__, - error); + __func__, error); goto parse_out; } break; @@ -778,9 +755,8 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) if (tlv_len != sizeof(uint32_t)) { error = EINVAL; device_printf(sc->sc_dev, - "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%d) < sizeof(uint32_t)\n", - __func__, - (int) tlv_len); + "%s: IWM_UCODE_TLV_PHY_SKU: tlv_len (%u) < sizeof(uint32_t)\n", + __func__, tlv_len); goto parse_out; } sc->sc_fw.phy_config = @@ -855,10 +831,10 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) goto out; } - sc->sc_fw.fw_sects[IWM_UCODE_REGULAR].paging_mem_size = + sc->sc_fw.img[IWM_UCODE_REGULAR].paging_mem_size = paging_mem_size; usniffer_img = IWM_UCODE_REGULAR_USNIFFER; - sc->sc_fw.fw_sects[usniffer_img].paging_mem_size = + sc->sc_fw.img[usniffer_img].paging_mem_size = paging_mem_size; break; @@ -905,12 +881,9 @@ iwm_read_firmware(struct iwm_softc *sc, enum iwm_ucode_type ucode_type) out: if (error) { - fw->fw_status = IWM_FW_STATUS_NONE; if (fw->fw_fp != NULL) iwm_fw_info_free(fw); - } else - fw->fw_status = IWM_FW_STATUS_DONE; - wakeup(&sc->sc_fw); + } return error; } @@ -1290,6 +1263,8 @@ iwm_stop_device(struct iwm_softc *sc) iv->phy_ctxt = NULL; iv->is_uploaded = 0; } + sc->sc_firmware_state = 0; + sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE; /* device going down, Stop using ICT table */ sc->sc_flags &= ~IWM_FLAG_USE_ICT; @@ -1917,8 +1892,6 @@ enum nvm_sku_bits { #define IWM_NVM_RF_CFG_TX_ANT_MSK_8000(x) ((x >> 24) & 0xF) #define IWM_NVM_RF_CFG_RX_ANT_MSK_8000(x) ((x >> 28) & 0xF) -#define DEFAULT_MAX_TX_POWER 16 - /** * enum iwm_nvm_channel_flags - channel flags in NVM * @IWM_NVM_CHANNEL_VALID: channel is usable for this SKU/geo @@ -2483,7 +2456,7 @@ iwm_pcie_load_firmware_chunk(struct iwm_softc *sc, uint32_t dst_addr, static int iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, - const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) + const struct iwm_fw_img *image, int cpu, int *first_ucode_section) { int shift_param; int i, ret = 0, sec_num = 0x1; @@ -2506,15 +2479,15 @@ iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, * PAGING_SEPARATOR_SECTION delimiter - separate between * CPU2 non paged to CPU2 paging sec. */ - if (!image->fw_sect[i].data || - image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || - image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { + if (!image->sec[i].data || + image->sec[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + image->sec[i].offset == IWM_PAGING_SEPARATOR_SECTION) { IWM_DPRINTF(sc, IWM_DEBUG_RESET, "Break since Data not valid or Empty section, sec = %d\n", i); break; } - ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); + ret = iwm_pcie_load_section(sc, i, &image->sec[i]); if (ret) return ret; @@ -2545,7 +2518,7 @@ iwm_pcie_load_cpu_sections_8000(struct iwm_softc *sc, static int iwm_pcie_load_cpu_sections(struct iwm_softc *sc, - const struct iwm_fw_sects *image, int cpu, int *first_ucode_section) + const struct iwm_fw_img *image, int cpu, int *first_ucode_section) { int shift_param; int i, ret = 0; @@ -2568,16 +2541,16 @@ iwm_pcie_load_cpu_sections(struct iwm_softc *sc, * PAGING_SEPARATOR_SECTION delimiter - separate between * CPU2 non paged to CPU2 paging sec. */ - if (!image->fw_sect[i].data || - image->fw_sect[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || - image->fw_sect[i].offset == IWM_PAGING_SEPARATOR_SECTION) { + if (!image->sec[i].data || + image->sec[i].offset == IWM_CPU1_CPU2_SEPARATOR_SECTION || + image->sec[i].offset == IWM_PAGING_SEPARATOR_SECTION) { IWM_DPRINTF(sc, IWM_DEBUG_RESET, "Break since Data not valid or Empty section, sec = %d\n", i); break; } - ret = iwm_pcie_load_section(sc, i, &image->fw_sect[i]); + ret = iwm_pcie_load_section(sc, i, &image->sec[i]); if (ret) return ret; } @@ -2589,8 +2562,7 @@ iwm_pcie_load_cpu_sections(struct iwm_softc *sc, } static int -iwm_pcie_load_given_ucode(struct iwm_softc *sc, - const struct iwm_fw_sects *image) +iwm_pcie_load_given_ucode(struct iwm_softc *sc, const struct iwm_fw_img *image) { int ret = 0; int first_ucode_section; @@ -2629,7 +2601,7 @@ iwm_pcie_load_given_ucode(struct iwm_softc *sc, int iwm_pcie_load_given_ucode_8000(struct iwm_softc *sc, - const struct iwm_fw_sects *image) + const struct iwm_fw_img *image) { int ret = 0; int first_ucode_section; @@ -2667,8 +2639,7 @@ iwm_enable_fw_load_int(struct iwm_softc *sc) /* XXX Add proper rfkill support code */ static int -iwm_start_fw(struct iwm_softc *sc, - const struct iwm_fw_sects *fw) +iwm_start_fw(struct iwm_softc *sc, const struct iwm_fw_img *fw) { int ret; @@ -2759,76 +2730,49 @@ static int iwm_alive_fn(struct iwm_softc *sc, struct iwm_rx_packet *pkt, void *data) { struct iwm_mvm_alive_data *alive_data = data; - struct iwm_mvm_alive_resp_ver1 *palive1; - struct iwm_mvm_alive_resp_ver2 *palive2; + struct iwm_mvm_alive_resp_v3 *palive3; struct iwm_mvm_alive_resp *palive; + struct iwm_umac_alive *umac; + struct iwm_lmac_alive *lmac1; + struct iwm_lmac_alive *lmac2 = NULL; + uint16_t status; - if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive1)) { - palive1 = (void *)pkt->data; - - sc->support_umac_log = FALSE; - sc->error_event_table = - le32toh(palive1->error_event_table_ptr); - sc->log_event_table = - le32toh(palive1->log_event_table_ptr); - alive_data->scd_base_addr = le32toh(palive1->scd_base_ptr); - - alive_data->valid = le16toh(palive1->status) == - IWM_ALIVE_STATUS_OK; - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "Alive VER1 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16toh(palive1->status), palive1->ver_type, - palive1->ver_subtype, palive1->flags); - } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive2)) { - palive2 = (void *)pkt->data; - sc->error_event_table = - le32toh(palive2->error_event_table_ptr); - sc->log_event_table = - le32toh(palive2->log_event_table_ptr); - alive_data->scd_base_addr = le32toh(palive2->scd_base_ptr); - sc->umac_error_event_table = - le32toh(palive2->error_info_addr); - - alive_data->valid = le16toh(palive2->status) == - IWM_ALIVE_STATUS_OK; - if (sc->umac_error_event_table) - sc->support_umac_log = TRUE; - - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "Alive VER2 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16toh(palive2->status), palive2->ver_type, - palive2->ver_subtype, palive2->flags); - - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - palive2->umac_major, palive2->umac_minor); - } else if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive)) { + if (iwm_rx_packet_payload_len(pkt) == sizeof(*palive)) { palive = (void *)pkt->data; - - sc->error_event_table = - le32toh(palive->error_event_table_ptr); - sc->log_event_table = - le32toh(palive->log_event_table_ptr); - alive_data->scd_base_addr = le32toh(palive->scd_base_ptr); - sc->umac_error_event_table = - le32toh(palive->error_info_addr); - - alive_data->valid = le16toh(palive->status) == - IWM_ALIVE_STATUS_OK; - if (sc->umac_error_event_table) - sc->support_umac_log = TRUE; - - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "Alive VER3 ucode status 0x%04x revision 0x%01X 0x%01X flags 0x%01X\n", - le16toh(palive->status), palive->ver_type, - palive->ver_subtype, palive->flags); - - IWM_DPRINTF(sc, IWM_DEBUG_RESET, - "UMAC version: Major - 0x%x, Minor - 0x%x\n", - le32toh(palive->umac_major), - le32toh(palive->umac_minor)); + umac = &palive->umac_data; + lmac1 = &palive->lmac_data[0]; + lmac2 = &palive->lmac_data[1]; + status = le16toh(palive->status); + } else { + palive3 = (void *)pkt->data; + umac = &palive3->umac_data; + lmac1 = &palive3->lmac_data; + status = le16toh(palive3->status); } + sc->error_event_table[0] = le32toh(lmac1->error_event_table_ptr); + if (lmac2) + sc->error_event_table[1] = + le32toh(lmac2->error_event_table_ptr); + sc->log_event_table = le32toh(lmac1->log_event_table_ptr); + sc->umac_error_event_table = le32toh(umac->error_info_addr); + alive_data->scd_base_addr = le32toh(lmac1->scd_base_ptr); + alive_data->valid = status == IWM_ALIVE_STATUS_OK; + if (sc->umac_error_event_table) + sc->support_umac_log = TRUE; + + IWM_DPRINTF(sc, IWM_DEBUG_FW, + "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n", + status, lmac1->ver_type, lmac1->ver_subtype); + + if (lmac2) + IWM_DPRINTF(sc, IWM_DEBUG_FW, "Alive ucode CDB\n"); + + IWM_DPRINTF(sc, IWM_DEBUG_FW, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + le32toh(umac->umac_major), + le32toh(umac->umac_minor)); + return TRUE; } @@ -2860,17 +2804,12 @@ iwm_mvm_load_ucode_wait_alive(struct iwm_softc *sc, { struct iwm_notification_wait alive_wait; struct iwm_mvm_alive_data alive_data; - const struct iwm_fw_sects *fw; + const struct iwm_fw_img *fw; enum iwm_ucode_type old_type = sc->cur_ucode; int error; static const uint16_t alive_cmd[] = { IWM_MVM_ALIVE }; - if ((error = iwm_read_firmware(sc, ucode_type)) != 0) { - device_printf(sc->sc_dev, "iwm_read_firmware: failed %d\n", - error); - return error; - } - fw = &sc->sc_fw.fw_sects[ucode_type]; + fw = &sc->sc_fw.img[ucode_type]; sc->cur_ucode = ucode_type; sc->ucode_loaded = FALSE; @@ -3043,6 +2982,19 @@ out: return ret; } +static int +iwm_mvm_config_ltr(struct iwm_softc *sc) +{ + struct iwm_ltr_config_cmd cmd = { + .flags = htole32(IWM_LTR_CFG_FLAG_FEATURE_ENABLE), + }; + + if (!sc->sc_ltr_enabled) + return 0; + + return iwm_mvm_send_cmd_pdu(sc, IWM_LTR_CONFIG, 0, sizeof(cmd), &cmd); +} + /* * receive side */ @@ -3324,7 +3276,11 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, struct iwm_mvm_tx_resp *tx_resp = (void *)pkt->data; struct ieee80211_ratectl_tx_status *txs = &sc->sc_txs; struct ieee80211_node *ni = &in->in_ni; + struct ieee80211vap *vap = ni->ni_vap; int status = le16toh(tx_resp->status.status) & IWM_TX_STATUS_MSK; + int new_rate, cur_rate = vap->iv_bss->ni_txrate; + boolean_t rate_matched; + uint8_t tx_resp_rate; KASSERT(tx_resp->frame_count == 1, ("too many frames")); @@ -3340,6 +3296,17 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, le32toh(tx_resp->initial_rate), (int) le16toh(tx_resp->wireless_media_time)); + tx_resp_rate = iwm_rate_from_ucode_rate(le32toh(tx_resp->initial_rate)); + + /* For rate control, ignore frames sent at different initial rate */ + rate_matched = (tx_resp_rate != 0 && tx_resp_rate == cur_rate); + + if (tx_resp_rate != 0 && cur_rate != 0 && !rate_matched) { + IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, + "tx_resp_rate doesn't match ni_txrate (tx_resp_rate=%u " + "ni_txrate=%d)\n", tx_resp_rate, cur_rate); + } + txs->flags = IEEE80211_RATECTL_STATUS_SHORT_RETRY | IEEE80211_RATECTL_STATUS_LONG_RETRY; txs->short_retries = tx_resp->failure_rts; @@ -3363,7 +3330,19 @@ iwm_mvm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt, } else { txs->status = IEEE80211_RATECTL_TX_SUCCESS; } - ieee80211_ratectl_tx_complete(ni, txs); + + if (rate_matched) { + int rix; + ieee80211_ratectl_tx_complete(ni, txs); + + rix = ieee80211_ratectl_rate(vap->iv_bss, NULL, 0); + new_rate = vap->iv_bss->ni_txrate; + if (new_rate != 0 && new_rate != cur_rate) { + struct iwm_node *in = IWM_NODE(vap->iv_bss); + iwm_setrates(sc, in, rix); + iwm_mvm_send_lq_cmd(sc, &in->in_lq, FALSE); + } + } return (txs->status != IEEE80211_RATECTL_TX_SUCCESS); } @@ -3489,37 +3468,6 @@ iwm_update_sched(struct iwm_softc *sc, int qid, int idx, uint8_t sta_id, } #endif -/* - * Take an 802.11 (non-n) rate, find the relevant rate - * table entry. return the index into in_ridx[]. - * - * The caller then uses that index back into in_ridx - * to figure out the rate index programmed /into/ - * the firmware for this given node. - */ -static int -iwm_tx_rateidx_lookup(struct iwm_softc *sc, struct iwm_node *in, - uint8_t rate) -{ - int i; - uint8_t r; - - for (i = 0; i < nitems(in->in_ridx); i++) { - r = iwm_rates[in->in_ridx[i]].rate; - if (rate == r) - return (i); - } - - IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, - "%s: couldn't find an entry for rate=%d\n", - __func__, - rate); - - /* XXX Return the first */ - /* XXX TODO: have it return the /lowest/ */ - return (0); -} - static int iwm_tx_rateidx_global_lookup(struct iwm_softc *sc, uint8_t rate) { @@ -3572,22 +3520,15 @@ iwm_tx_fill_cmd(struct iwm_softc *sc, struct iwm_node *in, IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: FIXED_RATE (%d)\n", __func__, tp->ucastrate); } else { - int i; - /* for data frames, use RS table */ IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: DATA\n", __func__); - /* XXX pass pktlen */ - (void) ieee80211_ratectl_rate(ni, NULL, 0); - i = iwm_tx_rateidx_lookup(sc, in, ni->ni_txrate); - ridx = in->in_ridx[i]; + ridx = iwm_rate2ridx(sc, ni->ni_txrate); + if (ridx == -1) + ridx = 0; /* This is the index into the programmed table */ - tx->initial_rate_index = i; + tx->initial_rate_index = 0; tx->tx_flags |= htole32(IWM_TX_CMD_FLG_STA_RATE); - - IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, - "%s: start with i=%d, txrate %d\n", - __func__, i, iwm_rates[ridx].rate); } IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TXRATE, @@ -3854,6 +3795,8 @@ iwm_raw_xmit(struct ieee80211_node *ni, struct mbuf *m, } else { error = iwm_tx(sc, m, ni, 0); } + if (sc->sc_tx_timer == 0) + callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); sc->sc_tx_timer = 5; IWM_UNLOCK(sc); @@ -3992,8 +3935,11 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) __func__, vap, ni); + IWM_DPRINTF(sc, IWM_DEBUG_STATE, "%s: Current node bssid: %s\n", + __func__, ether_sprintf(ni->ni_bssid)); in->in_assoc = 0; + iv->iv_auth = 1; /* * Firmware bug - it'll crash if the beacon interval is less @@ -4045,6 +3991,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) goto out; } } + sc->sc_firmware_state = 1; if ((error = iwm_mvm_phy_ctxt_changed(sc, &sc->sc_phyctxt[0], in->in_ni.ni_chan, 1, 1)) != 0) { @@ -4059,6 +4006,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) "%s: binding update cmd\n", __func__); goto out; } + sc->sc_firmware_state = 2; /* * Authentication becomes unreliable when powersaving is left enabled * here. Powersaving will be activated again when association has @@ -4078,6 +4026,7 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) "%s: failed to add sta\n", __func__); goto out; } + sc->sc_firmware_state = 3; /* * Prevent the FW from wandering off channel during association @@ -4085,86 +4034,16 @@ iwm_auth(struct ieee80211vap *vap, struct iwm_softc *sc) */ /* XXX duration is in units of TU, not MS */ duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS; - iwm_mvm_protect_session(sc, iv, duration, 500 /* XXX magic number */); - DELAY(100); + iwm_mvm_protect_session(sc, iv, duration, 500 /* XXX magic number */, TRUE); error = 0; out: + if (error != 0) + iv->iv_auth = 0; ieee80211_free_node(ni); return (error); } -static int -iwm_release(struct iwm_softc *sc, struct iwm_node *in) -{ - uint32_t tfd_msk; - - /* - * Ok, so *technically* the proper set of calls for going - * from RUN back to SCAN is: - * - * iwm_mvm_power_mac_disable(sc, in); - * iwm_mvm_mac_ctxt_changed(sc, vap); - * iwm_mvm_rm_sta(sc, in); - * iwm_mvm_update_quotas(sc, NULL); - * iwm_mvm_mac_ctxt_changed(sc, in); - * iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap)); - * iwm_mvm_mac_ctxt_remove(sc, in); - * - * However, that freezes the device not matter which permutations - * and modifications are attempted. Obviously, this driver is missing - * something since it works in the Linux driver, but figuring out what - * is missing is a little more complicated. Now, since we're going - * back to nothing anyway, we'll just do a complete device reset. - * Up your's, device! - */ - /* - * Just using 0xf for the queues mask is fine as long as we only - * get here from RUN state. - */ - tfd_msk = 0xf; - iwm_xmit_queue_drain(sc); - iwm_mvm_flush_tx_path(sc, tfd_msk, IWM_CMD_SYNC); - /* - * We seem to get away with just synchronously sending the - * IWM_TXPATH_FLUSH command. - */ -// iwm_trans_wait_tx_queue_empty(sc, tfd_msk); - iwm_stop_device(sc); - iwm_init_hw(sc); - if (in) - in->in_assoc = 0; - return 0; - -#if 0 - int error; - - iwm_mvm_power_mac_disable(sc, in); - - if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { - device_printf(sc->sc_dev, "mac ctxt change fail 1 %d\n", error); - return error; - } - - if ((error = iwm_mvm_rm_sta(sc, in)) != 0) { - device_printf(sc->sc_dev, "sta remove fail %d\n", error); - return error; - } - error = iwm_mvm_rm_sta(sc, in); - in->in_assoc = 0; - iwm_mvm_update_quotas(sc, NULL); - if ((error = iwm_mvm_mac_ctxt_changed(sc, vap)) != 0) { - device_printf(sc->sc_dev, "mac ctxt change fail 2 %d\n", error); - return error; - } - iwm_mvm_binding_remove_vif(sc, IWM_VAP(in->in_ni.ni_vap)); - - iwm_mvm_mac_ctxt_remove(sc, in); - - return error; -#endif -} - static struct ieee80211_node * iwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) { @@ -4172,6 +4051,19 @@ iwm_node_alloc(struct ieee80211vap *vap, const uint8_t mac[IEEE80211_ADDR_LEN]) M_NOWAIT | M_ZERO); } +static uint8_t +iwm_rate_from_ucode_rate(uint32_t rate_n_flags) +{ + uint8_t plcp = rate_n_flags & 0xff; + int i; + + for (i = 0; i <= IWM_RIDX_MAX; i++) { + if (iwm_rates[i].plcp == plcp) + return iwm_rates[i].rate; + } + return 0; +} + uint8_t iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx) { @@ -4187,15 +4079,36 @@ iwm_ridx2rate(struct ieee80211_rateset *rs, int ridx) return 0; } +static int +iwm_rate2ridx(struct iwm_softc *sc, uint8_t rate) +{ + int i; + + for (i = 0; i <= IWM_RIDX_MAX; i++) { + if (iwm_rates[i].rate == rate) + return i; + } + + device_printf(sc->sc_dev, + "%s: WARNING: device rate for %u not found!\n", + __func__, rate); + + return -1; +} + + static void -iwm_setrates(struct iwm_softc *sc, struct iwm_node *in) +iwm_setrates(struct iwm_softc *sc, struct iwm_node *in, int rix) { struct ieee80211_node *ni = &in->in_ni; struct iwm_lq_cmd *lq = &in->in_lq; - int nrates = ni->ni_rates.rs_nrates; + struct ieee80211_rateset *rs = &ni->ni_rates; + int nrates = rs->rs_nrates; int i, ridx, tab = 0; // int txant = 0; + KASSERT(rix >= 0 && rix < nrates, ("invalid rix")); + if (nrates > nitems(lq->rs_table)) { device_printf(sc->sc_dev, "%s: node supports %d rates, driver handles " @@ -4207,45 +4120,11 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in) "%s: node supports 0 rates, odd!\n", __func__); return; } + nrates = imin(rix + 1, nrates); - /* - * XXX .. and most of iwm_node is not initialised explicitly; - * it's all just 0x0 passed to the firmware. - */ - - /* first figure out which rates we should support */ - /* XXX TODO: this isn't 11n aware /at all/ */ - memset(&in->in_ridx, -1, sizeof(in->in_ridx)); IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, "%s: nrates=%d\n", __func__, nrates); - /* - * Loop over nrates and populate in_ridx from the highest - * rate to the lowest rate. Remember, in_ridx[] has - * IEEE80211_RATE_MAXSIZE entries! - */ - for (i = 0; i < min(nrates, IEEE80211_RATE_MAXSIZE); i++) { - int rate = ni->ni_rates.rs_rates[(nrates - 1) - i] & IEEE80211_RATE_VAL; - - /* Map 802.11 rate to HW rate index. */ - for (ridx = 0; ridx <= IWM_RIDX_MAX; ridx++) - if (iwm_rates[ridx].rate == rate) - break; - if (ridx > IWM_RIDX_MAX) { - device_printf(sc->sc_dev, - "%s: WARNING: device rate for %d not found!\n", - __func__, rate); - } else { - IWM_DPRINTF(sc, IWM_DEBUG_TXRATE, - "%s: rate: i: %d, rate=%d, ridx=%d\n", - __func__, - i, - rate, - ridx); - in->in_ridx[i] = ridx; - } - } - /* then construct a lq_cmd based on those */ memset(lq, 0, sizeof(*lq)); lq->sta_id = IWM_STATION_ID; @@ -4269,13 +4148,15 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in) * Note that we add the rates in the highest rate first * (opposite of ni_rates). */ - /* - * XXX TODO: this should be looping over the min of nrates - * and LQ_MAX_RETRY_NUM. Sigh. - */ for (i = 0; i < nrates; i++) { + int rate = rs->rs_rates[rix - i] & IEEE80211_RATE_VAL; int nextant; + /* Map 802.11 rate to HW rate index. */ + ridx = iwm_rate2ridx(sc, rate); + if (ridx == -1) + continue; + #if 0 if (txant == 0) txant = iwm_mvm_get_valid_tx_ant(sc); @@ -4284,12 +4165,6 @@ iwm_setrates(struct iwm_softc *sc, struct iwm_node *in) #else nextant = iwm_mvm_get_valid_tx_ant(sc); #endif - /* - * Map the rate id into a rate index into - * our hardware table containing the - * configuration to use for this rate. - */ - ridx = in->in_ridx[i]; tab = iwm_rates[ridx].plcp; tab |= nextant << IWM_RATE_MCS_ANT_POS; if (IWM_RIDX_IS_CCK(ridx)) @@ -4327,6 +4202,100 @@ iwm_media_change(struct ifnet *ifp) return error; } +static void +iwm_bring_down_firmware(struct iwm_softc *sc, struct ieee80211vap *vap) +{ + struct iwm_vap *ivp = IWM_VAP(vap); + int error; + + /* Avoid Tx watchdog triggering, when transfers get dropped here. */ + sc->sc_tx_timer = 0; + + ivp->iv_auth = 0; + if (sc->sc_firmware_state == 3) { + iwm_xmit_queue_drain(sc); +// iwm_mvm_flush_tx_path(sc, 0xf, IWM_CMD_SYNC); + error = iwm_mvm_rm_sta(sc, vap, TRUE); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to remove station: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + error = iwm_mvm_mac_ctxt_changed(sc, vap); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to change mac context: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + error = iwm_mvm_sf_update(sc, vap, FALSE); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to update smart FIFO: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + error = iwm_mvm_rm_sta_id(sc, vap); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to remove station id: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + error = iwm_mvm_update_quotas(sc, NULL); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to update PHY quota: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + /* XXX Might need to specify bssid correctly. */ + error = iwm_mvm_mac_ctxt_changed(sc, vap); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to change mac context: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state == 3) { + sc->sc_firmware_state = 2; + } + if (sc->sc_firmware_state > 1) { + error = iwm_mvm_binding_remove_vif(sc, ivp); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to remove channel ctx: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state > 1) { + sc->sc_firmware_state = 1; + } + ivp->phy_ctxt = NULL; + if (sc->sc_firmware_state > 0) { + error = iwm_mvm_mac_ctxt_changed(sc, vap); + if (error) { + device_printf(sc->sc_dev, + "%s: Failed to change mac context: %d\n", + __func__, error); + } + } + if (sc->sc_firmware_state > 0) { + error = iwm_mvm_power_update_mac(sc); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: failed to update power management\n", + __func__); + } + } + sc->sc_firmware_state = 0; +} static int iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) @@ -4338,115 +4307,73 @@ iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) int error; IWM_DPRINTF(sc, IWM_DEBUG_STATE, - "switching state %s -> %s\n", + "switching state %s -> %s arg=0x%x\n", ieee80211_state_name[vap->iv_state], - ieee80211_state_name[nstate]); + ieee80211_state_name[nstate], + arg); + IEEE80211_UNLOCK(ic); IWM_LOCK(sc); - if (vap->iv_state == IEEE80211_S_SCAN && nstate != vap->iv_state) + if ((sc->sc_flags & IWM_FLAG_SCAN_RUNNING) && + (nstate == IEEE80211_S_AUTH || + nstate == IEEE80211_S_ASSOC || + nstate == IEEE80211_S_RUN)) { + /* Stop blinking for a scan, when authenticating. */ iwm_led_blink_stop(sc); + } - /* disable beacon filtering if we're hopping out of RUN */ - if (vap->iv_state == IEEE80211_S_RUN && nstate != vap->iv_state) { + if (vap->iv_state == IEEE80211_S_RUN && nstate != IEEE80211_S_RUN) { + iwm_mvm_led_disable(sc); + /* disable beacon filtering if we're hopping out of RUN */ iwm_mvm_disable_beacon_filter(sc); - if (((in = IWM_NODE(vap->iv_bss)) != NULL)) in->in_assoc = 0; + } - if (nstate == IEEE80211_S_INIT) { - IWM_UNLOCK(sc); - IEEE80211_LOCK(ic); - error = ivp->iv_newstate(vap, nstate, arg); - IEEE80211_UNLOCK(ic); - IWM_LOCK(sc); - iwm_release(sc, NULL); - IWM_UNLOCK(sc); - IEEE80211_LOCK(ic); - return error; - } + if ((vap->iv_state == IEEE80211_S_AUTH || + vap->iv_state == IEEE80211_S_ASSOC || + vap->iv_state == IEEE80211_S_RUN) && + (nstate == IEEE80211_S_INIT || + nstate == IEEE80211_S_SCAN || + nstate == IEEE80211_S_AUTH)) { + iwm_mvm_stop_session_protection(sc, ivp); + } + if ((vap->iv_state == IEEE80211_S_RUN || + vap->iv_state == IEEE80211_S_ASSOC) && + nstate == IEEE80211_S_INIT) { /* - * It's impossible to directly go RUN->SCAN. If we iwm_release() - * above then the card will be completely reinitialized, - * so the driver must do everything necessary to bring the card - * from INIT to SCAN. - * - * Additionally, upon receiving deauth frame from AP, - * OpenBSD 802.11 stack puts the driver in IEEE80211_S_AUTH - * state. This will also fail with this driver, so bring the FSM - * from IEEE80211_S_RUN to IEEE80211_S_SCAN in this case as well. - * - * XXX TODO: fix this for FreeBSD! + * In this case, iv_newstate() wants to send an 80211 frame on + * the network that we are leaving. So we need to call it, + * before tearing down all the firmware state. */ - if (nstate == IEEE80211_S_SCAN || - nstate == IEEE80211_S_AUTH || - nstate == IEEE80211_S_ASSOC) { - IWM_DPRINTF(sc, IWM_DEBUG_STATE, - "Force transition to INIT; MGT=%d\n", arg); - IWM_UNLOCK(sc); - IEEE80211_LOCK(ic); - /* Always pass arg as -1 since we can't Tx right now. */ - /* - * XXX arg is just ignored anyway when transitioning - * to IEEE80211_S_INIT. - */ - vap->iv_newstate(vap, IEEE80211_S_INIT, -1); - IWM_DPRINTF(sc, IWM_DEBUG_STATE, - "Going INIT->SCAN\n"); - nstate = IEEE80211_S_SCAN; - IEEE80211_UNLOCK(ic); - IWM_LOCK(sc); - } + IWM_UNLOCK(sc); + IEEE80211_LOCK(ic); + ivp->iv_newstate(vap, nstate, arg); + IEEE80211_UNLOCK(ic); + IWM_LOCK(sc); + iwm_bring_down_firmware(sc, vap); + IWM_UNLOCK(sc); + IEEE80211_LOCK(ic); + return 0; } switch (nstate) { case IEEE80211_S_INIT: case IEEE80211_S_SCAN: - if (vap->iv_state == IEEE80211_S_AUTH || - vap->iv_state == IEEE80211_S_ASSOC) { - int myerr; - IWM_UNLOCK(sc); - IEEE80211_LOCK(ic); - myerr = ivp->iv_newstate(vap, nstate, arg); - IEEE80211_UNLOCK(ic); - IWM_LOCK(sc); - error = iwm_mvm_rm_sta(sc, vap, FALSE); - if (error) { - device_printf(sc->sc_dev, - "%s: Failed to remove station: %d\n", - __func__, error); - } - error = iwm_mvm_mac_ctxt_changed(sc, vap); - if (error) { - device_printf(sc->sc_dev, - "%s: Failed to change mac context: %d\n", - __func__, error); - } - error = iwm_mvm_binding_remove_vif(sc, ivp); - if (error) { - device_printf(sc->sc_dev, - "%s: Failed to remove channel ctx: %d\n", - __func__, error); - } - ivp->phy_ctxt = NULL; - error = iwm_mvm_power_update_mac(sc); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: failed to update power management\n", - __func__); - } - IWM_UNLOCK(sc); - IEEE80211_LOCK(ic); - return myerr; - } break; case IEEE80211_S_AUTH: + iwm_bring_down_firmware(sc, vap); if ((error = iwm_auth(vap, sc)) != 0) { device_printf(sc->sc_dev, "%s: could not move to auth state: %d\n", __func__, error); + iwm_bring_down_firmware(sc, vap); + IWM_UNLOCK(sc); + IEEE80211_LOCK(ic); + return 1; } break; @@ -4481,7 +4408,9 @@ iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) iwm_mvm_enable_beacon_filter(sc, ivp); iwm_mvm_power_update_mac(sc); iwm_mvm_update_quotas(sc, ivp); - iwm_setrates(sc, in); + { + int rix = ieee80211_ratectl_rate(&in->in_ni, NULL, 0); + iwm_setrates(sc, in, rix); if ((error = iwm_mvm_send_lq_cmd(sc, &in->in_lq, TRUE)) != 0) { device_printf(sc->sc_dev, @@ -4489,6 +4418,7 @@ iwm_newstate(struct ieee80211vap *vap, enum ieee80211_state nstate, int arg) } iwm_mvm_led_enable(sc); + } break; default: @@ -4529,7 +4459,7 @@ static boolean_t iwm_mvm_is_lar_supported(struct iwm_softc *sc) { boolean_t nvm_lar = sc->nvm_data->lar_enabled; - boolean_t tlv_lar = fw_has_capa(&sc->ucode_capa, + boolean_t tlv_lar = fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT); if (iwm_lar_disable) @@ -4548,9 +4478,9 @@ iwm_mvm_is_lar_supported(struct iwm_softc *sc) static boolean_t iwm_mvm_is_wifi_mcc_supported(struct iwm_softc *sc) { - return fw_has_api(&sc->ucode_capa, + return fw_has_api(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_API_WIFI_MCC_UPDATE) || - fw_has_capa(&sc->ucode_capa, + fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_LAR_MULTI_MCC); } @@ -4571,7 +4501,7 @@ iwm_send_update_mcc_cmd(struct iwm_softc *sc, const char *alpha2) int n_channels; uint16_t mcc; #endif - int resp_v2 = fw_has_capa(&sc->ucode_capa, + int resp_v2 = fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_LAR_SUPPORT_V2); if (!iwm_mvm_is_lar_supported(sc)) { @@ -4723,6 +4653,9 @@ iwm_init_hw(struct iwm_softc *sc) if (sc->cfg->device_family == IWM_DEVICE_FAMILY_7000) iwm_mvm_tt_tx_backoff(sc, 0); + if (iwm_mvm_config_ltr(sc) != 0) + device_printf(sc->sc_dev, "PCIe LTR configuration failed\n"); + error = iwm_mvm_power_update_device(sc); if (error) goto error; @@ -4730,7 +4663,7 @@ iwm_init_hw(struct iwm_softc *sc) if ((error = iwm_send_update_mcc_cmd(sc, "ZZ")) != 0) goto error; - if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { + if (fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { if ((error = iwm_mvm_config_umac_scan(sc)) != 0) goto error; } @@ -4806,7 +4739,6 @@ iwm_init(struct iwm_softc *sc) * Ok, firmware loaded and we are jogging */ sc->sc_flags |= IWM_FLAG_HW_INITED; - callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); } static int @@ -4852,6 +4784,10 @@ iwm_start(struct iwm_softc *sc) ieee80211_free_node(ni); continue; } + if (sc->sc_tx_timer == 0) { + callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, + sc); + } sc->sc_tx_timer = 15; } IWM_DPRINTF(sc, IWM_DEBUG_XMIT | IWM_DEBUG_TRACE, "<-%s\n", __func__); @@ -4876,6 +4812,9 @@ iwm_watchdog(void *arg) struct iwm_softc *sc = arg; struct ieee80211com *ic = &sc->sc_ic; + if (sc->sc_attached == 0) + return; + if (sc->sc_tx_timer > 0) { if (--sc->sc_tx_timer == 0) { device_printf(sc->sc_dev, "device timeout\n"); @@ -4886,8 +4825,8 @@ iwm_watchdog(void *arg) counter_u64_add(sc->sc_ic.ic_oerrors, 1); return; } + callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); } - callout_reset(&sc->sc_watchdog_to, hz, iwm_watchdog, sc); } static void @@ -5096,7 +5035,7 @@ iwm_nic_error(struct iwm_softc *sc) uint32_t base; device_printf(sc->sc_dev, "dumping device error log\n"); - base = sc->error_event_table; + base = sc->error_event_table[0]; if (base < 0x800000) { device_printf(sc->sc_dev, "Invalid error log pointer 0x%08x\n", base); @@ -5350,6 +5289,7 @@ iwm_handle_rxb(struct iwm_softc *sc, struct mbuf *m) case IWM_MAC_CONTEXT_CMD: case IWM_REPLY_SF_CFG_CMD: case IWM_POWER_TABLE_CMD: + case IWM_LTR_CONFIG: case IWM_PHY_CONTEXT_CMD: case IWM_BINDING_CONTEXT_CMD: case IWM_TIME_EVENT_CMD: @@ -5425,15 +5365,9 @@ iwm_handle_rxb(struct iwm_softc *sc, struct mbuf *m) break; } - case IWM_TIME_EVENT_NOTIFICATION: { - struct iwm_time_event_notif *notif; - notif = (void *)pkt->data; - - IWM_DPRINTF(sc, IWM_DEBUG_INTR, - "TE notif status = 0x%x action = 0x%x\n", - notif->status, notif->action); + case IWM_TIME_EVENT_NOTIFICATION: + iwm_mvm_rx_time_event_notif(sc, pkt); break; - } /* * Firmware versions 21 and 22 generate some DEBUG_LOG_MSG @@ -5611,6 +5545,8 @@ iwm_intr(void *arg) device_printf(sc->sc_dev, " 802.11 state %d\n", (vap == NULL) ? -1 : vap->iv_state); + /* Reset our firmware state tracking. */ + sc->sc_firmware_state = 0; /* Don't stop the device; just do a VAP restart */ IWM_UNLOCK(sc); @@ -5876,7 +5812,7 @@ iwm_attach(device_t dev) sc->sc_wantresp = -1; - /* Check device type */ + /* Match device id */ error = iwm_dev_check(dev); if (error != 0) goto fail; @@ -5888,19 +5824,18 @@ iwm_attach(device_t dev) * "dash" value). To keep hw_rev backwards compatible - we'll store it * in the old format. */ - if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) - sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | - (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); - - if (iwm_prepare_card_hw(sc) != 0) { - device_printf(dev, "could not initialize hardware\n"); - goto fail; - } - if (sc->cfg->device_family == IWM_DEVICE_FAMILY_8000) { int ret; uint32_t hw_step; + sc->sc_hw_rev = (sc->sc_hw_rev & 0xfff0) | + (IWM_CSR_HW_REV_STEP(sc->sc_hw_rev << 2) << 2); + + if (iwm_prepare_card_hw(sc) != 0) { + device_printf(dev, "could not initialize hardware\n"); + goto fail; + } + /* * In order to recognize C step the driver should read the * chip version id located at the AUX bus MISC address. @@ -6015,19 +5950,31 @@ iwm_attach(device_t dev) /* Max RSSI */ sc->sc_max_rssi = IWM_MAX_DBM - IWM_MIN_DBM; - sc->sc_preinit_hook.ich_func = iwm_preinit; - sc->sc_preinit_hook.ich_arg = sc; - if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { - device_printf(dev, "config_intrhook_establish failed\n"); - goto fail; - } - #ifdef IWM_DEBUG SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "debug", CTLFLAG_RW, &sc->sc_debug, 0, "control debugging"); #endif + error = iwm_read_firmware(sc); + if (error) { + goto fail; + } else if (sc->sc_fw.fw_fp == NULL) { + /* + * XXX Add a solution for properly deferring firmware load + * during bootup. + */ + goto fail; + } else { + sc->sc_preinit_hook.ich_func = iwm_preinit; + sc->sc_preinit_hook.ich_arg = sc; + if (config_intrhook_establish(&sc->sc_preinit_hook) != 0) { + device_printf(dev, + "config_intrhook_establish failed\n"); + goto fail; + } + } + IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_TRACE, "<-%s\n", __func__); @@ -6260,7 +6207,7 @@ iwm_scan_start(struct ieee80211com *ic) device_printf(sc->sc_dev, "%s: Previous scan not completed yet\n", __func__); } - if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) + if (fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) error = iwm_mvm_umac_scan(sc); else error = iwm_mvm_lmac_scan(sc); @@ -6353,6 +6300,10 @@ iwm_resume(device_t dev) * PCI Tx retries from interfering with C3 CPU state. */ pci_write_config(dev, PCI_CFG_RETRY_TIMEOUT, 0x00, 1); + + if (!sc->sc_attached) + return 0; + iwm_init_task(device_get_softc(dev)); IWM_LOCK(sc); @@ -6376,6 +6327,9 @@ iwm_suspend(device_t dev) do_stop = !! (sc->sc_ic.ic_nrunning > 0); + if (!sc->sc_attached) + return (0); + ieee80211_suspend_all(&sc->sc_ic); if (do_stop) { diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_config.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_config.h index 55421422d4..de537760e5 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_config.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_config.h @@ -80,6 +80,8 @@ enum iwm_device_family { IWM_DEVICE_FAMILY_8000, }; +#define IWM_DEFAULT_MAX_TX_POWER 22 + /* Antenna presence definitions */ #define IWM_ANT_NONE 0x0 #define IWM_ANT_A (1 << 0) diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_debug.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_debug.h index 39c63f21d7..7bda743d0b 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_debug.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_debug.h @@ -44,6 +44,7 @@ enum { IWM_DEBUG_TEMP = 0x00100000, /* Thermal Sensor handling */ IWM_DEBUG_FW = 0x00200000, /* Firmware management */ IWM_DEBUG_LAR = 0x00400000, /* Location Aware Regulatory */ + IWM_DEBUG_TE = 0x00800000, /* Time Event handling */ IWM_DEBUG_REGISTER = 0x20000000, /* print chipset register */ IWM_DEBUG_TRACE = 0x40000000, /* Print begin and start driver function */ IWM_DEBUG_FATAL = 0x80000000, /* fatal errors */ diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.c index d714d9377b..3a691bf200 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.c @@ -141,7 +141,7 @@ iwm_free_fw_paging(struct iwm_softc *sc) } static int -iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) +iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_img *image) { int sec_idx, idx; uint32_t offset = 0; @@ -158,7 +158,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) * CPU2 paging image (including instruction and data) */ for (sec_idx = 0; sec_idx < IWM_UCODE_SECTION_MAX; sec_idx++) { - if (image->fw_sect[sec_idx].offset == IWM_PAGING_SEPARATOR_SECTION) { + if (image->sec[sec_idx].offset == IWM_PAGING_SEPARATOR_SECTION) { sec_idx++; break; } @@ -168,7 +168,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) * If paging is enabled there should be at least 2 more sections left * (one for CSS and one for Paging data) */ - if (sec_idx >= nitems(image->fw_sect) - 1) { + if (sec_idx >= nitems(image->sec) - 1) { device_printf(sc->sc_dev, "Paging: Missing CSS and/or paging sections\n"); iwm_free_fw_paging(sc); @@ -181,7 +181,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) sec_idx); memcpy(sc->fw_paging_db[0].fw_paging_block.vaddr, - image->fw_sect[sec_idx].data, + image->sec[sec_idx].data, sc->fw_paging_db[0].fw_paging_size); IWM_DPRINTF(sc, IWM_DEBUG_FW, @@ -198,7 +198,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) */ for (idx = 1; idx < sc->num_of_paging_blk; idx++) { memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr, - (const char *)image->fw_sect[sec_idx].data + offset, + (const char *)image->sec[sec_idx].data + offset, sc->fw_paging_db[idx].fw_paging_size); IWM_DPRINTF(sc, IWM_DEBUG_FW, @@ -212,7 +212,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) /* copy the last paging block */ if (sc->num_of_pages_in_last_blk > 0) { memcpy(sc->fw_paging_db[idx].fw_paging_block.vaddr, - (const char *)image->fw_sect[sec_idx].data + offset, + (const char *)image->sec[sec_idx].data + offset, IWM_FW_PAGING_SIZE * sc->num_of_pages_in_last_blk); IWM_DPRINTF(sc, IWM_DEBUG_FW, @@ -224,7 +224,7 @@ iwm_fill_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) } static int -iwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) +iwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_img *image) { int blk_idx = 0; int error, num_of_pages; @@ -298,7 +298,7 @@ iwm_alloc_fw_paging_mem(struct iwm_softc *sc, const struct iwm_fw_sects *image) } int -iwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_sects *fw) +iwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_img *fw) { int ret; @@ -311,7 +311,7 @@ iwm_save_fw_paging(struct iwm_softc *sc, const struct iwm_fw_sects *fw) /* send paging cmd to FW in case CPU2 has paging image */ int -iwm_send_paging_cmd(struct iwm_softc *sc, const struct iwm_fw_sects *fw) +iwm_send_paging_cmd(struct iwm_softc *sc, const struct iwm_fw_img *fw) { int blk_idx; uint32_t dev_phy_addr; diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.h index 90a4666c8b..20c27db2f8 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_fw.h @@ -107,7 +107,7 @@ #define IWM_PAGING_TLV_SECURE_MASK 1 extern void iwm_free_fw_paging(struct iwm_softc *); -extern int iwm_save_fw_paging(struct iwm_softc *, const struct iwm_fw_sects *); -extern int iwm_send_paging_cmd(struct iwm_softc *, const struct iwm_fw_sects *); +extern int iwm_save_fw_paging(struct iwm_softc *, const struct iwm_fw_img *); +extern int iwm_send_paging_cmd(struct iwm_softc *, const struct iwm_fw_img *); #endif /* __IF_IWM_FW_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_led.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_led.c index ce14cdbaf1..2e95516959 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_led.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_led.c @@ -162,6 +162,9 @@ iwm_led_blink_timeout(void *arg) { struct iwm_softc *sc = arg; + if (sc->sc_attached == 0) + return; + if (iwm_mvm_led_is_enabled(sc)) iwm_mvm_led_disable(sc); else diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_mac_ctxt.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_mac_ctxt.c index 59b205d5da..2777a75c37 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_mac_ctxt.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_mac_ctxt.c @@ -309,7 +309,12 @@ iwm_mvm_mac_ctxt_cmd_common(struct iwm_softc *sc, struct iwm_node *in, * iwm_mvm_mac_ctxt_changed() when already authenticating or * associating, ni->ni_bssid should always make sense here. */ - IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); + if (ivp->iv_auth) { + IEEE80211_ADDR_COPY(cmd->bssid_addr, ni->ni_bssid); + } else { + /* XXX Or maybe all zeroes address? */ + IEEE80211_ADDR_COPY(cmd->bssid_addr, ieee80211broadcastaddr); + } #endif /* diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_pcie_trans.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_pcie_trans.c index 6ff4c42b3e..30c58de3ed 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_pcie_trans.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_pcie_trans.c @@ -406,18 +406,39 @@ iwm_prepare_card_hw(struct iwm_softc *sc) void iwm_apm_config(struct iwm_softc *sc) { - uint16_t reg; + uint16_t lctl, cap; + int pcie_ptr; - reg = pci_read_config(sc->sc_dev, PCIER_LINK_CTL, sizeof(reg)); - if (reg & PCIEM_LINK_CTL_ASPMC_L1) { - /* Um the Linux driver prints "Disabling L0S for this one ... */ + /* + * HW bug W/A for instability in PCIe bus L0S->L1 transition. + * Check if BIOS (or OS) enabled L1-ASPM on this device. + * If so (likely), disable L0S, so device moves directly L0->L1; + * costs negligible amount of power savings. + * If not (unlikely), enable L0S, so there is at least some + * power savings, even without L1. + */ + int error; + + error = pci_find_cap(sc->sc_dev, PCIY_EXPRESS, &pcie_ptr); + if (error != 0) + return; + lctl = pci_read_config(sc->sc_dev, pcie_ptr + PCIER_LINK_CTL, + sizeof(lctl)); + if (lctl & PCIEM_LINK_CTL_ASPMC_L1) { IWM_SETBITS(sc, IWM_CSR_GIO_REG, IWM_CSR_GIO_REG_VAL_L0S_ENABLED); } else { - /* ... and "Enabling" here */ IWM_CLRBITS(sc, IWM_CSR_GIO_REG, IWM_CSR_GIO_REG_VAL_L0S_ENABLED); } + + cap = pci_read_config(sc->sc_dev, pcie_ptr + PCIER_DEVICE_CTL2, + sizeof(cap)); + sc->sc_ltr_enabled = (cap & PCIEM_CTL2_LTR_ENABLE) ? 1 : 0; + IWM_DPRINTF(sc, IWM_DEBUG_RESET | IWM_DEBUG_PWRSAVE, + "L1 %sabled - LTR %sabled\n", + (lctl & PCIEM_LINK_CTL_ASPMC_L1) ? "En" : "Dis", + sc->sc_ltr_enabled ? "En" : "Dis"); } /* diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_phy_db.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_phy_db.c index 98c42259b4..3471e7d8de 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_phy_db.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_phy_db.c @@ -390,7 +390,7 @@ static uint8_t ch_id_to_ch_index(uint16_t ch_id) { if (!is_valid_channel(ch_id)) - return 0xff; + return 0xff; if (ch_id <= 14) return ch_id - 1; @@ -509,7 +509,7 @@ iwm_phy_db_send_all_channel_groups(struct iwm_phy_db *phy_db, int err; struct iwm_phy_db_entry *entry; - /* Send all the channel specific groups to operational fw */ + /* Send all the channel specific groups to operational fw */ for (i = 0; i < max_ch_groups; i++) { entry = iwm_phy_db_get_section(phy_db, type, diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_scan.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_scan.c index a5266b3442..ae410ca5be 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_scan.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_scan.c @@ -215,7 +215,7 @@ static inline boolean_t iwm_mvm_rrm_scan_needed(struct iwm_softc *sc) { /* require rrm scan whenever the fw supports it */ - return fw_has_capa(&sc->ucode_capa, + return fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT); } @@ -251,7 +251,7 @@ iwm_mvm_rx_lmac_scan_complete_notif(struct iwm_softc *sc, /* If this happens, the firmware has mistakenly sent an LMAC * notification during UMAC scans -- warn and ignore it. */ - if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { + if (fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) { device_printf(sc->sc_dev, "%s: Mistakenly got LMAC notification during UMAC scan\n", __func__); @@ -307,7 +307,8 @@ iwm_mvm_lmac_scan_fill_channels(struct iwm_softc *sc, int j; for (nchan = j = 0; - j < ss->ss_last && nchan < sc->ucode_capa.n_scan_channels; j++) { + j < ss->ss_last && nchan < sc->sc_fw.ucode_capa.n_scan_channels; + j++) { c = ss->ss_chans[j]; /* * Catch other channels, in case we have 900MHz channels or @@ -350,7 +351,8 @@ iwm_mvm_umac_scan_fill_channels(struct iwm_softc *sc, int j; for (nchan = j = 0; - j < ss->ss_last && nchan < sc->ucode_capa.n_scan_channels; j++) { + j < ss->ss_last && nchan < sc->sc_fw.ucode_capa.n_scan_channels; + j++) { c = ss->ss_chans[j]; /* * Catch other channels, in case we have 900MHz channels or @@ -495,7 +497,7 @@ iwm_mvm_config_umac_scan(struct iwm_softc *sc) IWM_SCAN_CONFIG_RATE_36M | IWM_SCAN_CONFIG_RATE_48M | IWM_SCAN_CONFIG_RATE_54M); - cmd_size = sizeof(*scan_config) + sc->ucode_capa.n_scan_channels; + cmd_size = sizeof(*scan_config) + sc->sc_fw.ucode_capa.n_scan_channels; scan_config = malloc(cmd_size, M_DEVBUF, M_NOWAIT | M_ZERO); if (scan_config == NULL) @@ -523,7 +525,8 @@ iwm_mvm_config_umac_scan(struct iwm_softc *sc) IWM_CHANNEL_FLAG_PRE_SCAN_PASSIVE2ACTIVE; for (nchan = j = 0; - j < ic->ic_nchans && nchan < sc->ucode_capa.n_scan_channels; j++) { + j < ic->ic_nchans && nchan < sc->sc_fw.ucode_capa.n_scan_channels; + j++) { c = &ic->ic_channels[j]; /* For 2GHz, only populate 11b channels */ /* For 5GHz, only populate 11a channels */ @@ -566,7 +569,7 @@ iwm_mvm_config_umac_scan(struct iwm_softc *sc) static boolean_t iwm_mvm_scan_use_ebs(struct iwm_softc *sc) { - const struct iwm_ucode_capabilities *capa = &sc->ucode_capa; + const struct iwm_ucode_capabilities *capa = &sc->sc_fw.ucode_capa; /* We can only use EBS if: * 1. the feature is supported; @@ -596,7 +599,7 @@ iwm_mvm_umac_scan(struct iwm_softc *sc) req_len = sizeof(struct iwm_scan_req_umac) + (sizeof(struct iwm_scan_channel_cfg_umac) * - sc->ucode_capa.n_scan_channels) + + sc->sc_fw.ucode_capa.n_scan_channels) + sizeof(struct iwm_scan_req_umac_tail); if (req_len > IWM_MAX_CMD_PAYLOAD_SIZE) return ENOMEM; @@ -630,7 +633,7 @@ iwm_mvm_umac_scan(struct iwm_softc *sc) tail = (void *)((char *)&req->data + sizeof(struct iwm_scan_channel_cfg_umac) * - sc->ucode_capa.n_scan_channels); + sc->sc_fw.ucode_capa.n_scan_channels); /* Check if we're doing an active directed scan. */ for (i = 0; i < nssid; i++) { @@ -694,7 +697,7 @@ iwm_mvm_lmac_scan(struct iwm_softc *sc) req_len = sizeof(struct iwm_scan_req_lmac) + (sizeof(struct iwm_scan_channel_cfg_lmac) * - sc->ucode_capa.n_scan_channels) + sizeof(struct iwm_scan_probe_req); + sc->sc_fw.ucode_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_NOWAIT | M_ZERO); @@ -764,7 +767,7 @@ iwm_mvm_lmac_scan(struct iwm_softc *sc) ret = iwm_mvm_fill_probe_req(sc, (struct iwm_scan_probe_req *)(req->data + (sizeof(struct iwm_scan_channel_cfg_lmac) * - sc->ucode_capa.n_scan_channels))); + sc->sc_fw.ucode_capa.n_scan_channels))); if (ret) { free(req, M_DEVBUF); return ret; @@ -863,7 +866,7 @@ iwm_mvm_scan_stop_wait(struct iwm_softc *sc) IWM_DPRINTF(sc, IWM_DEBUG_SCAN, "Preparing to stop scan\n"); - if (fw_has_capa(&sc->ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) + if (fw_has_capa(&sc->sc_fw.ucode_capa, IWM_UCODE_TLV_CAPA_UMAC_SCAN)) ret = iwm_mvm_umac_scan_abort(sc); else ret = iwm_mvm_lmac_scan_abort(sc); diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_sta.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_sta.c index 1482074aae..1eb30aa139 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_sta.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_sta.c @@ -286,7 +286,7 @@ iwm_mvm_rm_sta(struct iwm_softc *sc, struct ieee80211vap *vap, for (ac = 0; ac < WME_NUM_AC; ac++) { tfd_queue_msk |= htole32(1 << iwm_mvm_ac_to_tx_fifo[ac]); } - ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, 0); + ret = iwm_mvm_flush_tx_path(sc, tfd_queue_msk, IWM_CMD_SYNC); if (ret) return ret; #ifdef notyet /* function not yet implemented */ diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.c b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.c index 0d2b491acc..d9a9b520ba 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.c +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.c @@ -155,31 +155,188 @@ __FBSDID("$FreeBSD: releng/12.0/sys/dev/iwm/if_iwm_time_event.c 318212 2017-05-1 #include #include #include +#include #include #include +#define TU_TO_HZ(tu) (((uint64_t)(tu) * 1024 * hz) / 1000000) + +static void +iwm_mvm_te_clear_data(struct iwm_softc *sc) +{ + sc->sc_time_event_uid = 0; + sc->sc_time_event_duration = 0; + sc->sc_time_event_end_ticks = 0; + sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE; +} + /* - * For the high priority TE use a time event type that has similar priority to - * the FW's action scan priority. + * Handles a FW notification for an event that is known to the driver. + * + * @mvm: the mvm component + * @te_data: the time event data + * @notif: the notification data corresponding the time event data. */ -#define IWM_MVM_ROC_TE_TYPE_NORMAL IWM_TE_P2P_DEVICE_DISCOVERABLE -#define IWM_MVM_ROC_TE_TYPE_MGMT_TX IWM_TE_P2P_CLIENT_ASSOC +static void +iwm_mvm_te_handle_notif(struct iwm_softc *sc, + struct iwm_time_event_notif *notif) +{ + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "Handle time event notif - UID = 0x%x action %d\n", + le32toh(notif->unique_id), + le32toh(notif->action)); + + if (!le32toh(notif->status)) { + const char *msg; + + if (notif->action & htole32(IWM_TE_V2_NOTIF_HOST_EVENT_START)) + msg = "Time Event start notification failure"; + else + msg = "Time Event end notification failure"; + + IWM_DPRINTF(sc, IWM_DEBUG_TE, "%s\n", msg); + } + + if (le32toh(notif->action) & IWM_TE_V2_NOTIF_HOST_EVENT_END) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "TE ended - current time %d, estimated end %d\n", + ticks, sc->sc_time_event_end_ticks); + + iwm_mvm_te_clear_data(sc); + } else if (le32toh(notif->action) & IWM_TE_V2_NOTIF_HOST_EVENT_START) { + sc->sc_time_event_end_ticks = + ticks + TU_TO_HZ(sc->sc_time_event_duration); + } else { + device_printf(sc->sc_dev, "Got TE with unknown action\n"); + } +} + +/* + * The Rx handler for time event notifications + */ +void +iwm_mvm_rx_time_event_notif(struct iwm_softc *sc, struct iwm_rx_packet *pkt) +{ + struct iwm_time_event_notif *notif = (void *)pkt->data; + + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "Time event notification - UID = 0x%x action %d\n", + le32toh(notif->unique_id), + le32toh(notif->action)); + + iwm_mvm_te_handle_notif(sc, notif); +} + +static int +iwm_mvm_te_notif(struct iwm_softc *sc, struct iwm_rx_packet *pkt, + void *data) +{ + struct iwm_time_event_notif *resp; + int resp_len = iwm_rx_packet_payload_len(pkt); + + if (pkt->hdr.code != IWM_TIME_EVENT_NOTIFICATION || + resp_len != sizeof(*resp)) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "Invalid TIME_EVENT_NOTIFICATION response\n"); + return 1; + } + + resp = (void *)pkt->data; + + /* te_data->uid is already set in the TIME_EVENT_CMD response */ + if (le32toh(resp->unique_id) != sc->sc_time_event_uid) + return false; + + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "TIME_EVENT_NOTIFICATION response - UID = 0x%x\n", + sc->sc_time_event_uid); + if (!resp->status) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "TIME_EVENT_NOTIFICATION received but not executed\n"); + } + + return 1; +} + +static int +iwm_mvm_time_event_response(struct iwm_softc *sc, struct iwm_rx_packet *pkt, + void *data) +{ + struct iwm_time_event_resp *resp; + int resp_len = iwm_rx_packet_payload_len(pkt); + + if (pkt->hdr.code != IWM_TIME_EVENT_CMD || + resp_len != sizeof(*resp)) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "Invalid TIME_EVENT_CMD response\n"); + return 1; + } + + resp = (void *)pkt->data; + + /* we should never get a response to another TIME_EVENT_CMD here */ + if (le32toh(resp->id) != IWM_TE_BSS_STA_AGGRESSIVE_ASSOC) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "Got TIME_EVENT_CMD response with wrong id: %d\n", + le32toh(resp->id)); + return 0; + } + + sc->sc_time_event_uid = le32toh(resp->unique_id); + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "TIME_EVENT_CMD response - UID = 0x%x\n", sc->sc_time_event_uid); + return 1; +} + + +/* XXX Use the te_data function argument properly, like in iwlwifi's code. */ static int iwm_mvm_time_event_send_add(struct iwm_softc *sc, struct iwm_vap *ivp, void *te_data, struct iwm_time_event_cmd *te_cmd) { + static const uint16_t time_event_response[] = { IWM_TIME_EVENT_CMD }; + struct iwm_notification_wait wait_time_event; int ret; - IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, + IWM_DPRINTF(sc, IWM_DEBUG_TE, "Add new TE, duration %d TU\n", le32toh(te_cmd->duration)); - ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, IWM_CMD_SYNC, - sizeof(*te_cmd), te_cmd); + sc->sc_time_event_duration = le32toh(te_cmd->duration); + + /* + * Use a notification wait, which really just processes the + * command response and doesn't wait for anything, in order + * to be able to process the response and get the UID inside + * the RX path. Using CMD_WANT_SKB doesn't work because it + * stores the buffer and then wakes up this thread, by which + * time another notification (that the time event started) + * might already be processed unsuccessfully. + */ + iwm_init_notification_wait(sc->sc_notif_wait, &wait_time_event, + time_event_response, + nitems(time_event_response), + iwm_mvm_time_event_response, /*te_data*/NULL); + + ret = iwm_mvm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, 0, sizeof(*te_cmd), + te_cmd); if (ret) { - IWM_DPRINTF(sc, IWM_DEBUG_CMD | IWM_DEBUG_RESET, + IWM_DPRINTF(sc, IWM_DEBUG_TE, "%s: Couldn't send IWM_TIME_EVENT_CMD: %d\n", __func__, ret); + iwm_remove_notification(sc->sc_notif_wait, &wait_time_event); + return ret; + } + + /* No need to wait for anything, so just pass 1 (0 isn't valid) */ + IWM_UNLOCK(sc); + ret = iwm_wait_notification(sc->sc_notif_wait, &wait_time_event, 1); + IWM_LOCK(sc); + /* should never fail */ + if (ret) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "%s: Failed to get response for IWM_TIME_EVENT_CMD: %d\n", + __func__, ret); } return ret; @@ -187,10 +344,16 @@ iwm_mvm_time_event_send_add(struct iwm_softc *sc, struct iwm_vap *ivp, void iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_vap *ivp, - uint32_t duration, uint32_t max_delay) + uint32_t duration, uint32_t max_delay, boolean_t wait_for_notif) { + const uint16_t te_notif_response[] = { IWM_TIME_EVENT_NOTIFICATION }; + struct iwm_notification_wait wait_te_notif; struct iwm_time_event_cmd time_cmd = {}; + /* Do nothing if a time event is already scheduled. */ + if (sc->sc_flags & IWM_FLAG_TE_ACTIVE) + return; + time_cmd.action = htole32(IWM_FW_CTXT_ACTION_ADD); time_cmd.id_and_color = htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)); @@ -209,5 +372,58 @@ iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_vap *ivp, IWM_TE_V2_NOTIF_HOST_EVENT_END | IWM_T2_V2_START_IMMEDIATELY); - iwm_mvm_time_event_send_add(sc, ivp, /*te_data*/NULL, &time_cmd); + if (!wait_for_notif) { + iwm_mvm_time_event_send_add(sc, ivp, /*te_data*/NULL, &time_cmd); + DELAY(100); + sc->sc_flags |= IWM_FLAG_TE_ACTIVE; + return; + } + + /* + * Create notification_wait for the TIME_EVENT_NOTIFICATION to use + * right after we send the time event + */ + iwm_init_notification_wait(sc->sc_notif_wait, &wait_te_notif, + te_notif_response, nitems(te_notif_response), + iwm_mvm_te_notif, /*te_data*/NULL); + + /* If TE was sent OK - wait for the notification that started */ + if (iwm_mvm_time_event_send_add(sc, ivp, /*te_data*/NULL, &time_cmd)) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "%s: Failed to add TE to protect session\n", __func__); + iwm_remove_notification(sc->sc_notif_wait, &wait_te_notif); + } else { + sc->sc_flags |= IWM_FLAG_TE_ACTIVE; + IWM_UNLOCK(sc); + if (iwm_wait_notification(sc->sc_notif_wait, &wait_te_notif, + TU_TO_HZ(max_delay))) { + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "%s: Failed to protect session until TE\n", + __func__); + } + IWM_LOCK(sc); + } +} + +void +iwm_mvm_stop_session_protection(struct iwm_softc *sc, struct iwm_vap *ivp) +{ + struct iwm_time_event_cmd time_cmd = {}; + + /* Do nothing if the time event has already ended. */ + if ((sc->sc_flags & IWM_FLAG_TE_ACTIVE) == 0) + return; + + time_cmd.id = htole32(sc->sc_time_event_uid); + time_cmd.action = htole32(IWM_FW_CTXT_ACTION_REMOVE); + time_cmd.id_and_color = + htole32(IWM_FW_CMD_ID_AND_COLOR(ivp->id, ivp->color)); + + IWM_DPRINTF(sc, IWM_DEBUG_TE, + "%s: Removing TE 0x%x\n", __func__, le32toh(time_cmd.id)); + if (iwm_mvm_send_cmd_pdu(sc, IWM_TIME_EVENT_CMD, 0, sizeof(time_cmd), + &time_cmd) == 0) + iwm_mvm_te_clear_data(sc); + + DELAY(100); } diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.h index e9f5937611..43ac2fb526 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwm_time_event.h @@ -107,7 +107,11 @@ #ifndef __IF_IWM_TIME_EVENT_H__ #define __IF_IWM_TIME_EVENT_H__ +extern void iwm_mvm_rx_time_event_notif(struct iwm_softc *sc, + struct iwm_rx_packet *pkt); extern void iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_vap *ivp, - uint32_t duration, uint32_t max_delay); + uint32_t duration, uint32_t max_delay, boolean_t wait_for_notif); +extern void iwm_mvm_stop_session_protection(struct iwm_softc *sc, + struct iwm_vap *ivp); #endif /* __IF_IWM_TIME_EVENT_H__ */ diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmreg.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmreg.h index bc2444ea98..82581467fc 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmreg.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmreg.h @@ -702,8 +702,9 @@ enum iwm_ucode_tlv_api { * @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_UCODE_TLV_CAPA_TX_POWER_ACK: reduced TX power API has larger + * command size (command version 4) that supports toggling ACK TX + * power reduction. * * @IWM_NUM_UCODE_TLV_CAPA: number of bits used */ @@ -744,9 +745,9 @@ enum iwm_ucode_tlv_capa { 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_UCODE_TLV_CAPA_TX_POWER_ACK = 84, IWM_NUM_UCODE_TLV_CAPA = 128 }; @@ -1790,15 +1791,11 @@ enum { /* Power - legacy power table command */ IWM_POWER_TABLE_CMD = 0x77, IWM_PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION = 0x78, + IWM_LTR_CONFIG = 0xee, /* Thermal Throttling*/ IWM_REPLY_THERMAL_MNG_BACKOFF = 0x7e, - /* Scanning */ - IWM_SCAN_ABORT_CMD = 0x81, - IWM_SCAN_START_NOTIFICATION = 0x82, - IWM_SCAN_RESULTS_NOTIFICATION = 0x83, - /* NVM */ IWM_NVM_ACCESS_CMD = 0x88, @@ -1869,8 +1866,6 @@ enum { IWM_NET_DETECT_PROFILES_CMD = 0x57, IWM_NET_DETECT_HOTSPOTS_CMD = 0x58, IWM_NET_DETECT_HOTSPOTS_QUERY_CMD = 0x59, - - IWM_REPLY_MAX = 0xff, }; enum iwm_phy_ops_subcmd_ids { @@ -1921,6 +1916,51 @@ struct iwm_reduce_tx_power_cmd { uint16_t pwr_restriction; } __packed; /* IWM_TX_REDUCED_POWER_API_S_VER_1 */ +enum iwm_dev_tx_power_cmd_mode { + IWM_TX_POWER_MODE_SET_MAC = 0, + IWM_TX_POWER_MODE_SET_DEVICE = 1, + IWM_TX_POWER_MODE_SET_CHAINS = 2, + IWM_TX_POWER_MODE_SET_ACK = 3, +}; /* TX_POWER_REDUCED_FLAGS_TYPE_API_E_VER_4 */; + +#define IWM_NUM_CHAIN_LIMITS 2 +#define IWM_NUM_SUB_BANDS 5 + +/** + * struct iwm_dev_tx_power_cmd - TX power reduction command + * @set_mode: see &enum iwl_dev_tx_power_cmd_mode + * @mac_context_id: id of the mac ctx for which we are reducing TX power. + * @pwr_restriction: TX power restriction in 1/8 dBms. + * @dev_24: device TX power restriction in 1/8 dBms + * @dev_52_low: device TX power restriction upper band - low + * @dev_52_high: device TX power restriction upper band - high + * @per_chain_restriction: per chain restrictions + */ +struct iwm_dev_tx_power_cmd_v3 { + uint32_t set_mode; + uint32_t mac_context_id; + uint16_t pwr_restriction; + uint16_t dev_24; + uint16_t dev_52_low; + uint16_t dev_52_high; + uint16_t per_chain_restriction[IWM_NUM_CHAIN_LIMITS][IWM_NUM_SUB_BANDS]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_3 */ + +#define IWM_DEV_MAX_TX_POWER 0x7FFF + +/** + * struct iwm_dev_tx_power_cmd - TX power reduction command + * @v3: version 3 of the command, embedded here for easier software handling + * @enable_ack_reduction: enable or disable close range ack TX power + * reduction. + */ +struct iwm_dev_tx_power_cmd { + /* v4 is just an extension of v3 - keep this here */ + struct iwm_dev_tx_power_cmd_v3 v3; + uint8_t enable_ack_reduction; + uint8_t reserved[3]; +} __packed; /* TX_REDUCED_POWER_API_S_VER_4 */ + /* * Calibration control struct. * Sent as part of the phy configuration command. @@ -2102,41 +2142,13 @@ enum { #define IWM_ALIVE_FLG_RFKILL (1 << 0) -struct iwm_mvm_alive_resp_ver1 { - 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; +struct iwm_lmac_alive { + uint32_t ucode_major; + uint32_t ucode_minor; 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 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 */ -} __packed; /* IWM_ALIVE_RES_API_S_VER_1 */ - -struct iwm_mvm_alive_resp_ver2 { - 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 */ @@ -2146,36 +2158,28 @@ struct iwm_mvm_alive_resp_ver2 { 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 */ +} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_3 */ + +struct iwm_umac_alive { + uint32_t umac_major; /* UMAC version: major */ + uint32_t umac_minor; /* UMAC version: minor */ uint32_t error_info_addr; /* SRAM address for UMAC error log */ uint32_t dbg_print_buff_addr; -} __packed; /* ALIVE_RES_API_S_VER_2 */ +} __packed; /* UMAC_ALIVE_DATA_API_S_VER_2 */ + +struct iwm_mvm_alive_resp_v3 { + uint16_t status; + uint16_t flags; + struct iwm_lmac_alive lmac_data; + struct iwm_umac_alive umac_data; +} __packed; /* ALIVE_RES_API_S_VER_3 */ struct iwm_mvm_alive_resp { 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 */ + struct iwm_lmac_alive lmac_data[2]; + struct iwm_umac_alive umac_data; +} __packed; /* ALIVE_RES_API_S_VER_4 */ /* Error response/notification */ enum { @@ -3527,6 +3531,57 @@ struct iwm_nonqos_seq_query_cmd { /* Power Management Commands, Responses, Notifications */ +/** + * enum iwm_ltr_config_flags - masks for LTR config command flags + * @IWM_LTR_CFG_FLAG_FEATURE_ENABLE: Feature operational status + * @IWM_LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS: allow LTR change on shadow + * memory access + * @IWM_LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH: allow LTR msg send on ANY LTR + * reg change + * @IWM_LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3: allow LTR msg send on transition from + * D0 to D3 + * @IWM_LTR_CFG_FLAG_SW_SET_SHORT: fixed static short LTR register + * @IWM_LTR_CFG_FLAG_SW_SET_LONG: fixed static short LONG register + * @IWM_LTR_CFG_FLAG_DENIE_C10_ON_PD: allow going into C10 on PD + */ +enum iwm_ltr_config_flags { + IWM_LTR_CFG_FLAG_FEATURE_ENABLE = (1 << 0), + IWM_LTR_CFG_FLAG_HW_DIS_ON_SHADOW_REG_ACCESS = (1 << 1), + IWM_LTR_CFG_FLAG_HW_EN_SHRT_WR_THROUGH = (1 << 2), + IWM_LTR_CFG_FLAG_HW_DIS_ON_D0_2_D3 = (1 << 3), + IWM_LTR_CFG_FLAG_SW_SET_SHORT = (1 << 4), + IWM_LTR_CFG_FLAG_SW_SET_LONG = (1 << 5), + IWM_LTR_CFG_FLAG_DENIE_C10_ON_PD = (1 << 6), +}; + +/** + * struct iwm_ltr_config_cmd_v1 - configures the LTR + * @flags: See %enum iwm_ltr_config_flags + */ +struct iwm_ltr_config_cmd_v1 { + uint32_t flags; + uint32_t static_long; + uint32_t static_short; +} __packed; /* LTR_CAPABLE_API_S_VER_1 */ + +#define IWM_LTR_VALID_STATES_NUM 4 + +/** + * struct iwm_ltr_config_cmd - configures the LTR + * @flags: See %enum iwm_ltr_config_flags + * @static_long: + * @static_short: + * @ltr_cfg_values: + * @ltr_short_idle_timeout: + */ +struct iwm_ltr_config_cmd { + uint32_t flags; + uint32_t static_long; + uint32_t static_short; + uint32_t ltr_cfg_values[IWM_LTR_VALID_STATES_NUM]; + uint32_t ltr_short_idle_timeout; +} __packed; /* LTR_CAPABLE_API_S_VER_2 */ + /* Radio LP RX Energy Threshold measured in dBm */ #define IWM_POWER_LPRX_RSSI_THRESHOLD 75 #define IWM_POWER_LPRX_RSSI_THRESHOLD_MAX 94 @@ -4228,29 +4283,6 @@ enum iwm_tx_pm_timeouts { #define IWM_TX_CMD_SEC_WEP_KEY_IDX_MSK 0xc0 #define IWM_TX_CMD_SEC_KEY128 0x08 -/* TODO: how does these values are OK with only 16 bit variable??? */ -/* - * TX command next frame info - * - * bits 0:2 - security control (IWM_TX_CMD_SEC_*) - * bit 3 - immediate ACK required - * bit 4 - rate is taken from STA table - * bit 5 - frame belongs to BA stream - * bit 6 - immediate BA response expected - * bit 7 - unused - * bits 8:15 - Station ID - * bits 16:31 - rate - */ -#define IWM_TX_CMD_NEXT_FRAME_ACK_MSK (0x8) -#define IWM_TX_CMD_NEXT_FRAME_STA_RATE_MSK (0x10) -#define IWM_TX_CMD_NEXT_FRAME_BA_MSK (0x20) -#define IWM_TX_CMD_NEXT_FRAME_IMM_BA_RSP_MSK (0x40) -#define IWM_TX_CMD_NEXT_FRAME_FLAGS_MSK (0xf8) -#define IWM_TX_CMD_NEXT_FRAME_STA_ID_MSK (0xff00) -#define IWM_TX_CMD_NEXT_FRAME_STA_ID_POS (8) -#define IWM_TX_CMD_NEXT_FRAME_RATE_MSK (0xffff0000) -#define IWM_TX_CMD_NEXT_FRAME_RATE_POS (16) - /* * TX command Frame life time in us - to be written in pm_frame_timeout */ @@ -4288,7 +4320,7 @@ enum iwm_tx_pm_timeouts { * @initial_rate_index: index into the rate table for initial TX attempt. * Applied if IWM_TX_CMD_FLG_STA_RATE_MSK is set, normally 0 for data frames. * @key: security key - * @next_frame_flags: IWM_TX_CMD_SEC_* and IWM_TX_CMD_NEXT_FRAME_* + * @reserved3: reserved * @life_time: frame life time (usecs??) * @dram_lsb_ptr: Physical address of scratch area in the command (try_cnt + * btkill_cnd + reserved), first 32 bits. "0" disables usage. @@ -4952,50 +4984,14 @@ struct iwm_periodic_scan_complete { uint32_t reserved; } __packed; -/* How many statistics are gathered for each channel */ -#define IWM_SCAN_RESULTS_STATISTICS 1 - /** - * enum iwm_scan_complete_status - status codes for scan complete notifications - * @IWM_SCAN_COMP_STATUS_OK: scan completed successfully - * @IWM_SCAN_COMP_STATUS_ABORT: scan was aborted by user - * @IWM_SCAN_COMP_STATUS_ERR_SLEEP: sending null sleep packet failed - * @IWM_SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT: timeout before channel is ready - * @IWM_SCAN_COMP_STATUS_ERR_PROBE: sending probe request failed - * @IWM_SCAN_COMP_STATUS_ERR_WAKEUP: sending null wakeup packet failed - * @IWM_SCAN_COMP_STATUS_ERR_ANTENNAS: invalid antennas chosen at scan command - * @IWM_SCAN_COMP_STATUS_ERR_INTERNAL: internal error caused scan abort - * @IWM_SCAN_COMP_STATUS_ERR_COEX: medium was lost ot WiMax - * @IWM_SCAN_COMP_STATUS_P2P_ACTION_OK: P2P public action frame TX was successful - * (not an error!) - * @IWM_SCAN_COMP_STATUS_ITERATION_END: indicates end of one repeatition the driver - * asked for - * @IWM_SCAN_COMP_STATUS_ERR_ALLOC_TE: scan could not allocate time events -*/ -enum iwm_scan_complete_status { - IWM_SCAN_COMP_STATUS_OK = 0x1, - IWM_SCAN_COMP_STATUS_ABORT = 0x2, - IWM_SCAN_COMP_STATUS_ERR_SLEEP = 0x3, - IWM_SCAN_COMP_STATUS_ERR_CHAN_TIMEOUT = 0x4, - IWM_SCAN_COMP_STATUS_ERR_PROBE = 0x5, - IWM_SCAN_COMP_STATUS_ERR_WAKEUP = 0x6, - IWM_SCAN_COMP_STATUS_ERR_ANTENNAS = 0x7, - IWM_SCAN_COMP_STATUS_ERR_INTERNAL = 0x8, - IWM_SCAN_COMP_STATUS_ERR_COEX = 0x9, - IWM_SCAN_COMP_STATUS_P2P_ACTION_OK = 0xA, - IWM_SCAN_COMP_STATUS_ITERATION_END = 0x0B, - IWM_SCAN_COMP_STATUS_ERR_ALLOC_TE = 0x0C, -}; - -/** - * struct iwm_scan_results_notif - scan results for one channel - * ( IWM_SCAN_RESULTS_NOTIFICATION = 0x83 ) + * struct iwm_scan_results_notif - scan results for one channel - + * SCAN_RESULT_NTF_API_S_VER_3 * @channel: which channel the results are from * @band: 0 for 5.2 GHz, 1 for 2.4 GHz * @probe_status: IWM_SCAN_PROBE_STATUS_*, indicates success of probe request * @num_probe_not_sent: # of request that weren't sent due to not enough time * @duration: duration spent in channel, in usecs - * @statistics: statistics gathered for this channel */ struct iwm_scan_results_notif { uint8_t channel; @@ -5003,8 +4999,7 @@ struct iwm_scan_results_notif { uint8_t probe_status; uint8_t num_probe_not_sent; uint32_t duration; - uint32_t statistics[IWM_SCAN_RESULTS_STATISTICS]; -} __packed; /* IWM_SCAN_RESULT_NTF_API_S_VER_2 */ +} __packed; enum iwm_scan_framework_client { IWM_SCAN_CLIENT_SCHED_SCAN = (1 << 0), diff --git a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmvar.h b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmvar.h index 61d50d90bf..c660a07565 100644 --- a/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmvar.h +++ b/src/add-ons/kernel/drivers/network/wlan/idualwifi7260/dev/iwm/if_iwmvar.h @@ -139,15 +139,6 @@ struct iwm_tx_radiotap_header { #define IWM_UCODE_SECTION_MAX 16 -/* - * fw_status is used to determine if we've already parsed the firmware file - * - * In addition to the following, status < 0 ==> -error - */ -#define IWM_FW_STATUS_NONE 0 -#define IWM_FW_STATUS_INPROGRESS 1 -#define IWM_FW_STATUS_DONE 2 - /** * enum iwm_ucode_type * @@ -195,16 +186,20 @@ struct iwm_fw_desc { uint32_t offset; /* offset in the device */ }; +struct iwm_fw_img { + struct iwm_fw_desc sec[IWM_UCODE_SECTION_MAX]; + int fw_count; + int is_dual_cpus; + uint32_t paging_mem_size; +}; + struct iwm_fw_info { const struct firmware *fw_fp; - int fw_status; - struct iwm_fw_sects { - struct iwm_fw_desc fw_sect[IWM_UCODE_SECTION_MAX]; - int fw_count; - int is_dual_cpus; - uint32_t paging_mem_size; - } fw_sects[IWM_UCODE_TYPE_MAX]; + /* ucode images */ + struct iwm_fw_img img[IWM_UCODE_TYPE_MAX]; + + struct iwm_ucode_capabilities ucode_capa; uint32_t phy_config; uint8_t valid_tx_ant; @@ -370,6 +365,7 @@ struct iwm_bf_data { struct iwm_vap { struct ieee80211vap iv_vap; int is_uploaded; + int iv_auth; int (*iv_newstate)(struct ieee80211vap *, enum ieee80211_state, int); @@ -404,8 +400,6 @@ struct iwm_node { int in_assoc; struct iwm_lq_cmd in_lq; - - uint8_t in_ridx[IEEE80211_RATE_MAXSIZE]; }; #define IWM_NODE(_ni) ((struct iwm_node *)(_ni)) @@ -440,6 +434,7 @@ struct iwm_softc { #define IWM_FLAG_BUSY (1 << 4) #define IWM_FLAG_SCANNING (1 << 5) #define IWM_FLAG_SCAN_RUNNING (1 << 6) +#define IWM_FLAG_TE_ACTIVE (1 << 7) struct intr_config_hook sc_preinit_hook; struct callout sc_watchdog_to; @@ -482,7 +477,6 @@ struct iwm_softc { int ucode_loaded; char sc_fwver[32]; - struct iwm_ucode_capabilities ucode_capa; char sc_fw_mcc[3]; int sc_intmask; @@ -544,7 +538,7 @@ struct iwm_softc { int cmd_hold_nic_awake; /* Firmware status */ - uint32_t error_event_table; + uint32_t error_event_table[2]; uint32_t log_event_table; uint32_t umac_error_event_table; int support_umac_log; @@ -565,6 +559,20 @@ struct iwm_softc { /* Indicate if device power save is allowed */ boolean_t sc_ps_disabled; + int sc_ltr_enabled; + + /* Track firmware state for STA association. */ + int sc_firmware_state; + + /* Unique ID (assigned by the firmware) of the current Time Event. */ + uint32_t sc_time_event_uid; + + /* Duration of the Time Event in TU. */ + uint32_t sc_time_event_duration; + + /* Expected end of the Time Event in HZ ticks. */ + int sc_time_event_end_ticks; + #ifdef __HAIKU__ uint32_t sc_intr_status_1; uint32_t sc_intr_status_2;