diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm')
19 files changed, 547 insertions, 227 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 7a56a0ac151c..ca2d11f4984e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1795,12 +1795,6 @@ iwl_mvm_get_wakeup_status(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return ERR_PTR(ret); } - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - fw_status = ERR_PTR(-ERFKILL); - goto out_free_resp; - } - status_size = sizeof(*fw_status); len = iwl_rx_packet_payload_len(cmd.resp_pkt); @@ -1925,12 +1919,6 @@ iwl_mvm_netdetect_query_results(struct iwl_mvm *mvm, return ret; } - /* RF-kill already asserted again... */ - if (!cmd.resp_pkt) { - ret = -ERFKILL; - goto out_free_resp; - } - len = iwl_rx_packet_payload_len(cmd.resp_pkt); if (len < sizeof(*query)) { IWL_ERR(mvm, "Invalid scan offload profiles query response!\n"); @@ -2087,9 +2075,8 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) bool keep = false; bool unified_image = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); - - u32 flags = CMD_ASYNC | CMD_HIGH_PRIO | CMD_SEND_IN_IDLE | - CMD_WAKE_UP_TRANS; + bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_D0I3_END_FIRST); mutex_lock(&mvm->mutex); @@ -2110,6 +2097,15 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) /* query SRAM first in case we want event logging */ iwl_mvm_read_d3_sram(mvm); + if (d0i3_first) { + ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, 0, 0, NULL); + if (ret < 0) { + IWL_ERR(mvm, "Failed to send D0I3_END_CMD first (%d)\n", + ret); + goto err; + } + } + /* * Query the current location and source from the D3 firmware so we * can play it back when we re-intiailize the D0 firmware @@ -2155,9 +2151,14 @@ out_iterate: iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); out: + /* no need to reset the device in unified images, if successful */ if (unified_image && !ret) { - ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, flags, 0, NULL); - if (!ret) /* D3 ended successfully - no need to reset device */ + /* nothing else to do if we already sent D0I3_END_CMD */ + if (d0i3_first) + return 0; + + ret = iwl_mvm_send_cmd_pdu(mvm, D0I3_END_CMD, 0, 0, NULL); + if (!ret) return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c index 5d475b4850ae..a7ac281e5cde 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs-vif.c @@ -7,7 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 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 @@ -34,7 +34,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH - * Copyright(c) 2016 Intel Deutschland GmbH + * Copyright(c) 2016 - 2017 Intel Deutschland GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1304,11 +1304,11 @@ static ssize_t iwl_dbgfs_low_latency_read(struct file *file, char buf[30] = {}; int len; - len = snprintf(buf, sizeof(buf) - 1, - "traffic=%d\ndbgfs=%d\nvcmd=%d\n", - mvmvif->low_latency_traffic, - mvmvif->low_latency_dbgfs, - mvmvif->low_latency_vcmd); + len = scnprintf(buf, sizeof(buf) - 1, + "traffic=%d\ndbgfs=%d\nvcmd=%d\n", + mvmvif->low_latency_traffic, + mvmvif->low_latency_dbgfs, + mvmvif->low_latency_vcmd); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } @@ -1385,10 +1385,12 @@ static ssize_t iwl_dbgfs_rx_phyinfo_read(struct file *file, struct ieee80211_vif *vif = file->private_data; struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); char buf[8]; + int len; - snprintf(buf, sizeof(buf), "0x%04x\n", mvmvif->mvm->dbgfs_rx_phyinfo); + len = scnprintf(buf, sizeof(buf), "0x%04x\n", + mvmvif->mvm->dbgfs_rx_phyinfo); - return simple_read_from_buffer(user_buf, count, ppos, buf, sizeof(buf)); + return simple_read_from_buffer(user_buf, count, ppos, buf, len); } static void iwl_dbgfs_quota_check(void *data, u8 *mac, @@ -1439,7 +1441,7 @@ static ssize_t iwl_dbgfs_quota_min_read(struct file *file, char buf[10]; int len; - len = snprintf(buf, sizeof(buf), "%d\n", mvmvif->dbgfs_quota_min); + len = scnprintf(buf, sizeof(buf), "%d\n", mvmvif->dbgfs_quota_min); return simple_read_from_buffer(user_buf, count, ppos, buf, len); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 744dc069ff23..c2a1aeef74ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -119,19 +119,30 @@ static ssize_t iwl_dbgfs_tx_flush_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { int ret; - u32 scd_q_msk; + u32 flush_arg; if (!iwl_mvm_firmware_running(mvm) || mvm->cur_ucode != IWL_UCODE_REGULAR) return -EIO; - if (sscanf(buf, "%x", &scd_q_msk) != 1) + if (kstrtou32(buf, 0, &flush_arg)) return -EINVAL; - IWL_ERR(mvm, "FLUSHING queues: scd_q_msk = 0x%x\n", scd_q_msk); + if (iwl_mvm_has_new_tx_api(mvm)) { + IWL_DEBUG_TX_QUEUES(mvm, + "FLUSHING all tids queues on sta_id = %d\n", + flush_arg); + mutex_lock(&mvm->mutex); + ret = iwl_mvm_flush_sta_tids(mvm, flush_arg, 0xFF, 0) ? : count; + mutex_unlock(&mvm->mutex); + return ret; + } + + IWL_DEBUG_TX_QUEUES(mvm, "FLUSHING queues mask to flush = 0x%x\n", + flush_arg); mutex_lock(&mvm->mutex); - ret = iwl_mvm_flush_tx_path(mvm, scd_q_msk, 0) ? : count; + ret = iwl_mvm_flush_tx_path(mvm, flush_arg, 0) ? : count; mutex_unlock(&mvm->mutex); return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h index aa5aaf7c940d..1d970bf0d735 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-mac.h @@ -199,6 +199,7 @@ struct iwl_mac_data_ibss { * @dtim_reciprocal: 2^32 / dtim_interval , applicable only when associated * @listen_interval: in beacon intervals, applicable only when associated * @assoc_id: unique ID assigned by the AP during association + * @assoc_beacon_arrive_time: TSF of first beacon after association */ struct iwl_mac_data_sta { __le32 is_assoc; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h index d27c67b67015..f8e92026bdc6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-scan.h @@ -769,7 +769,7 @@ struct iwl_scan_offload_profiles_query { * @last_channel: last channel that was scanned * @start_tsf: TSF timer in usecs of the scan start time for the mac specified * in &struct iwl_scan_req_umac. - * @results: array of scan results, only "scanned_channels" of them are valid + * @results: array of scan results, length in @scanned_channels */ struct iwl_umac_scan_iter_complete_notif { __le32 uid; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index c30e0e95b0b0..a37c584b0b48 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -488,7 +488,7 @@ enum iwl_tx_agg_status { /** * struct agg_tx_status - per packet TX aggregation status - * @status: enum iwl_tx_agg_status + * @status: See &enum iwl_tx_agg_status * @sequence: Sequence # for this frame's Tx cmd (not SSN!) */ struct agg_tx_status { @@ -513,7 +513,7 @@ struct agg_tx_status { #define IWL_MVM_TX_RES_GET_RA(_ra_tid) ((_ra_tid) >> 4) /** - * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet + * struct iwl_mvm_tx_resp_v3 - notifies that fw is TXing a packet * ( REPLY_TX = 0x1c ) * @frame_count: 1 no aggregation, >1 aggregation * @bt_kill_count: num of times blocked by bluetooth (unused for agg) @@ -540,7 +540,63 @@ struct agg_tx_status { * @tx_queue: TX queue for this response * @status: for non-agg: frame status TX_STATUS_* * for agg: status of 1st frame, AGG_TX_STATE_*; other frame status fields - * follow this one, up to frame_count. + * follow this one, up to frame_count. Length in @frame_count. + * + * After the array of statuses comes the SSN of the SCD. Look at + * %iwl_mvm_get_scd_ssn for more details. + */ +struct iwl_mvm_tx_resp_v3 { + u8 frame_count; + u8 bt_kill_count; + u8 failure_rts; + u8 failure_frame; + __le32 initial_rate; + __le16 wireless_media_time; + + u8 pa_status; + u8 pa_integ_res_a[3]; + u8 pa_integ_res_b[3]; + u8 pa_integ_res_c[3]; + __le16 measurement_req_id; + u8 reduced_tpc; + u8 reserved; + + __le32 tfd_info; + __le16 seq_ctl; + __le16 byte_cnt; + u8 tlc_info; + u8 ra_tid; + __le16 frame_ctrl; + struct agg_tx_status status[]; +} __packed; /* TX_RSP_API_S_VER_3 */ + +/** + * struct iwl_mvm_tx_resp - notifies that fw is TXing a packet + * ( REPLY_TX = 0x1c ) + * @frame_count: 1 no aggregation, >1 aggregation + * @bt_kill_count: num of times blocked by bluetooth (unused for agg) + * @failure_rts: num of failures due to unsuccessful RTS + * @failure_frame: num failures due to no ACK (unused for agg) + * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the + * Tx of all the batch. RATE_MCS_* + * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. + * for agg: RTS + CTS + aggregation tx time + block-ack time. + * in usec. + * @pa_status: tx power info + * @pa_integ_res_a: tx power info + * @pa_integ_res_b: tx power info + * @pa_integ_res_c: tx power info + * @measurement_req_id: tx power info + * @reduced_tpc: transmit power reduction used + * @reserved: reserved + * @tfd_info: TFD information set by the FH + * @seq_ctl: sequence control from the Tx cmd + * @byte_cnt: byte count from the Tx cmd + * @tlc_info: TLC rate info + * @ra_tid: bits [3:0] = ra, bits [7:4] = tid + * @frame_ctrl: frame control + * @tx_queue: TX queue for this response + * @status: for non-agg: frame status TX_STATUS_* * For version 6 TX response isn't received for aggregation at all. * * After the array of statuses comes the SSN of the SCD. Look at @@ -568,16 +624,9 @@ struct iwl_mvm_tx_resp { u8 tlc_info; u8 ra_tid; __le16 frame_ctrl; - union { - struct { - struct agg_tx_status status; - } v3;/* TX_RSP_API_S_VER_3 */ - struct { - __le16 tx_queue; - __le16 reserved2; - struct agg_tx_status status; - } v6; - }; + __le16 tx_queue; + __le16 reserved2; + struct agg_tx_status status; } __packed; /* TX_RSP_API_S_VER_6 */ /** @@ -797,12 +846,24 @@ enum iwl_dump_control { * @flush_ctl: control flags * @reserved: reserved */ -struct iwl_tx_path_flush_cmd { +struct iwl_tx_path_flush_cmd_v1 { __le32 queues_ctl; __le16 flush_ctl; __le16 reserved; } __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_1 */ +/** + * struct iwl_tx_path_flush_cmd -- queue/FIFO flush command + * @sta_id: station ID to flush + * @tid_mask: TID mask to flush + * @reserved: reserved + */ +struct iwl_tx_path_flush_cmd { + __le32 sta_id; + __le16 tid_mask; + __le16 reserved; +} __packed; /* TX_PATH_FLUSH_CMD_API_S_VER_2 */ + /* Available options for the SCD_QUEUE_CFG HCMD */ enum iwl_scd_cfg_actions { SCD_CFG_DISABLE_QUEUE = 0x0, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index cc6af9bd4e10..b84e8ddbbbc9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -216,7 +216,9 @@ enum iwl_legacy_cmds { FW_GET_ITEM_CMD = 0x1a, /** - * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2 + * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2, + * response in &struct iwl_mvm_tx_resp or + * &struct iwl_mvm_tx_resp_v3 */ TX_CMD = 0x1c, @@ -552,9 +554,26 @@ enum iwl_regulatory_and_nvm_subcmd_ids { NVM_GET_INFO = 0x2, }; +/** + * enum iwl_debug_cmds - debug commands + */ enum iwl_debug_cmds { + /** + * @LMAC_RD_WR: + * LMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and + * &struct iwl_dbg_mem_access_rsp + */ LMAC_RD_WR = 0x0, + /** + * @UMAC_RD_WR: + * UMAC memory read/write, using &struct iwl_dbg_mem_access_cmd and + * &struct iwl_dbg_mem_access_rsp + */ UMAC_RD_WR = 0x1, + /** + * @MFU_ASSERT_DUMP_NTF: + * &struct iwl_mfu_assert_dump_notif + */ MFU_ASSERT_DUMP_NTF = 0xFE, }; @@ -2111,7 +2130,7 @@ struct ct_kill_notif { * enum ctdp_cmd_operation - CTDP command operations * @CTDP_CMD_OPERATION_START: update the current budget * @CTDP_CMD_OPERATION_STOP: stop ctdp -* @CTDP_CMD_OPERATION_REPORT: get the avgerage budget +* @CTDP_CMD_OPERATION_REPORT: get the average budget */ enum iwl_mvm_ctdp_cmd_operation { CTDP_CMD_OPERATION_START = 0x1, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c index c66baf1e1443..1602b360353c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-dbg.c @@ -319,10 +319,8 @@ static void iwl_mvm_dump_fifos(struct iwl_mvm *mvm, void iwl_mvm_free_fw_dump_desc(struct iwl_mvm *mvm) { - if (mvm->fw_dump_desc == &iwl_mvm_dump_desc_assert) - return; - - kfree(mvm->fw_dump_desc); + if (mvm->fw_dump_desc != &iwl_mvm_dump_desc_assert) + kfree(mvm->fw_dump_desc); mvm->fw_dump_desc = NULL; } @@ -915,6 +913,10 @@ int iwl_mvm_fw_dbg_collect_desc(struct iwl_mvm *mvm, if (trigger) delay = msecs_to_jiffies(le32_to_cpu(trigger->stop_delay)); + if (WARN(mvm->trans->state == IWL_TRANS_NO_FW, + "Can't collect dbg data when FW isn't alive\n")) + return -EIO; + if (test_and_set_bit(IWL_MVM_STATUS_DUMPING_FW_LOG, &mvm->status)) return -EBUSY; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index baf2c39a82fd..71f23ac608eb 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -846,6 +846,8 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, cpu_to_le64(vif->bss_conf.sync_tsf + dtim_offs); ctxt_sta->dtim_time = cpu_to_le32(vif->bss_conf.sync_device_ts + dtim_offs); + ctxt_sta->assoc_beacon_arrive_time = + cpu_to_le32(vif->bss_conf.sync_device_ts); IWL_DEBUG_INFO(mvm, "DTIM TBTT is 0x%llx/0x%x, offset %d\n", le64_to_cpu(ctxt_sta->dtim_tsf), @@ -1457,6 +1459,7 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, beacon_notify_hdr = &beacon->beacon_notify_hdr; mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); + mvm->ibss_manager = beacon->ibss_mgr_status != 0; agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 08f86e77eba5..d04a88c6c593 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1988,14 +1988,32 @@ static void iwl_mvm_bss_info_changed_station(struct iwl_mvm *mvm, WARN_ONCE(iwl_mvm_sf_update(mvm, vif, false), "Failed to update SF upon disassociation\n"); - /* remove AP station now that the MAC is unassoc */ - ret = iwl_mvm_rm_sta_id(mvm, vif, mvmvif->ap_sta_id); - if (ret) - IWL_ERR(mvm, "failed to remove AP station\n"); + /* + * If we get an assert during the connection (after the + * station has been added, but before the vif is set + * to associated), mac80211 will re-add the station and + * then configure the vif. Since the vif is not + * associated, we would remove the station here and + * this would fail the recovery. + */ + if (!test_bit(IWL_MVM_STATUS_IN_HW_RESTART, + &mvm->status)) { + /* + * Remove AP station now that + * the MAC is unassoc + */ + ret = iwl_mvm_rm_sta_id(mvm, vif, + mvmvif->ap_sta_id); + if (ret) + IWL_ERR(mvm, + "failed to remove AP station\n"); + + if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) + mvm->d0i3_ap_sta_id = + IWL_MVM_INVALID_STA; + mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; + } - if (mvm->d0i3_ap_sta_id == mvmvif->ap_sta_id) - mvm->d0i3_ap_sta_id = IWL_MVM_INVALID_STA; - mvmvif->ap_sta_id = IWL_MVM_INVALID_STA; /* remove quota for this interface */ ret = iwl_mvm_update_quotas(mvm, false, NULL); if (ret) @@ -2395,7 +2413,7 @@ static void __iwl_mvm_mac_sta_notify(struct ieee80211_hw *hw, __set_bit(tid_data->txq_id, &txqs); - if (iwl_mvm_tid_queued(tid_data) == 0) + if (iwl_mvm_tid_queued(mvm, tid_data) == 0) continue; __set_bit(tid, &tids); @@ -2883,7 +2901,8 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, case WLAN_CIPHER_SUITE_CCMP: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: - key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; + if (!iwl_mvm_has_new_tx_api(mvm)) + key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; break; case WLAN_CIPHER_SUITE_AES_CMAC: case WLAN_CIPHER_SUITE_BIP_GMAC_128: @@ -2929,8 +2948,13 @@ static int iwl_mvm_mac_set_key(struct ieee80211_hw *hw, ret = -EOPNOTSUPP; else ret = 0; - key->hw_key_idx = STA_KEY_IDX_INVALID; - break; + + if (key->cipher != WLAN_CIPHER_SUITE_GCMP && + key->cipher != WLAN_CIPHER_SUITE_GCMP_256 && + !iwl_mvm_has_new_tx_api(mvm)) { + key->hw_key_idx = STA_KEY_IDX_INVALID; + break; + } } /* During FW restart, in order to restore the state as it was, @@ -3731,6 +3755,13 @@ static int iwl_mvm_switch_vif_chanctx(struct ieee80211_hw *hw, return ret; } +static int iwl_mvm_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); + + return mvm->ibss_manager; +} + static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) @@ -4264,11 +4295,13 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, goto out; } - if (notif->sync) + if (notif->sync) { ret = wait_event_timeout(mvm->rx_sync_waitq, - atomic_read(&mvm->queue_sync_counter) == 0, + atomic_read(&mvm->queue_sync_counter) == 0 || + iwl_mvm_is_radio_killed(mvm), HZ); - WARN_ON_ONCE(!ret); + WARN_ON_ONCE(!ret && !iwl_mvm_is_radio_killed(mvm)); + } out: atomic_set(&mvm->queue_sync_counter, 0); @@ -4332,6 +4365,8 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .join_ibss = iwl_mvm_start_ap_ibss, .leave_ibss = iwl_mvm_stop_ap_ibss, + .tx_last_beacon = iwl_mvm_tx_last_beacon, + .set_tim = iwl_mvm_set_tim, .channel_switch = iwl_mvm_channel_switch, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 9a9163f64553..3b1f15873034 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1021,6 +1021,9 @@ struct iwl_mvm { /* system time of last beacon (for AP/GO interface) */ u32 ap_last_beacon_gp2; + /* indicates that we transmitted the last beacon */ + bool ibss_manager; + bool lar_regdom_set; enum iwl_mcc_source mcc_src; @@ -1078,6 +1081,18 @@ struct iwl_mvm { #define IWL_MAC80211_GET_MVM(_hw) \ IWL_OP_MODE_GET_MVM((struct iwl_op_mode *)((_hw)->priv)) +/** + * enum iwl_mvm_status - MVM status bits + * @IWL_MVM_STATUS_HW_RFKILL: HW RF-kill is asserted + * @IWL_MVM_STATUS_HW_CTKILL: CT-kill is active + * @IWL_MVM_STATUS_ROC_RUNNING: remain-on-channel is running + * @IWL_MVM_STATUS_IN_HW_RESTART: HW restart is active + * @IWL_MVM_STATUS_IN_D0I3: NIC is in D0i3 + * @IWL_MVM_STATUS_ROC_AUX_RUNNING: AUX remain-on-channel is running + * @IWL_MVM_STATUS_D3_RECONFIG: D3 reconfiguration is being done + * @IWL_MVM_STATUS_DUMPING_FW_LOG: FW log is being dumped + * @IWL_MVM_STATUS_FIRMWARE_RUNNING: firmware is running + */ enum iwl_mvm_status { IWL_MVM_STATUS_HW_RFKILL, IWL_MVM_STATUS_HW_CTKILL, @@ -1281,14 +1296,13 @@ static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) IWL_UCODE_TLV_CAPA_CDB_SUPPORT); } -static inline struct agg_tx_status* -iwl_mvm_get_agg_status(struct iwl_mvm *mvm, - struct iwl_mvm_tx_resp *tx_resp) +static inline struct agg_tx_status * +iwl_mvm_get_agg_status(struct iwl_mvm *mvm, void *tx_resp) { if (iwl_mvm_has_new_tx_api(mvm)) - return &tx_resp->v6.status; + return &((struct iwl_mvm_tx_resp *)tx_resp)->status; else - return &tx_resp->v3.status; + return ((struct iwl_mvm_tx_resp_v3 *)tx_resp)->status; } static inline bool iwl_mvm_is_tt_in_fw(struct iwl_mvm *mvm) @@ -1370,7 +1384,9 @@ const char *iwl_mvm_get_tx_fail_reason(u32 status); static inline const char *iwl_mvm_get_tx_fail_reason(u32 status) { return ""; } #endif int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags); -int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool int_sta, u32 flags); +int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal, u32 flags); +int iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id, + u16 tids, u32 flags); void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); @@ -1730,7 +1746,7 @@ static inline bool iwl_mvm_vif_low_latency(struct iwl_mvm_vif *mvmvif) } /* hw scheduler queue config */ -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, +bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, unsigned int wdg_timeout); int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 87b9ebfc653e..dac7e542a190 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -118,10 +118,6 @@ static int iwl_nvm_write_chunk(struct iwl_mvm *mvm, u16 section, return ret; pkt = cmd.resp_pkt; - if (!pkt) { - IWL_ERR(mvm, "Error in NVM_ACCESS response\n"); - return -EINVAL; - } /* Extract & check NVM write response */ nvm_resp = (void *)pkt->data; if (le16_to_cpu(nvm_resp->status) != READ_NVM_CHUNK_SUCCEED) { @@ -600,9 +596,11 @@ int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm) if (!is_valid_ether_addr(mvm->nvm_data->hw_addr)) { IWL_ERR(trans, "no valid mac address was found\n"); ret = -EINVAL; - goto out; + goto err_free; } + IWL_INFO(trans, "base HW address: %pM\n", mvm->nvm_data->hw_addr); + /* Initialize general data */ mvm->nvm_data->nvm_version = le16_to_cpu(rsp->general.nvm_version); @@ -632,7 +630,11 @@ int iwl_mvm_nvm_get_from_fw(struct iwl_mvm *mvm) mvm->nvm_data->valid_rx_ant & mvm->fw->valid_rx_ant, rsp->regulatory.lar_enabled && lar_fw_supported); - ret = 0; + iwl_free_resp(&hcmd); + return 0; + +err_free: + kfree(mvm->nvm_data); out: iwl_free_resp(&hcmd); return ret; @@ -783,6 +785,10 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * sizeof(__le32); resp_cp = kmemdup(mcc_resp, resp_len, GFP_KERNEL); + if (!resp_cp) { + resp_cp = ERR_PTR(-ENOMEM); + goto exit; + } } else { struct iwl_mcc_update_resp_v1 *mcc_resp_v1 = (void *)pkt->data; @@ -790,21 +796,18 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, resp_len = sizeof(struct iwl_mcc_update_resp) + n_channels * sizeof(__le32); resp_cp = kzalloc(resp_len, GFP_KERNEL); - - if (resp_cp) { - resp_cp->status = mcc_resp_v1->status; - resp_cp->mcc = mcc_resp_v1->mcc; - resp_cp->cap = mcc_resp_v1->cap; - resp_cp->source_id = mcc_resp_v1->source_id; - resp_cp->n_channels = mcc_resp_v1->n_channels; - memcpy(resp_cp->channels, mcc_resp_v1->channels, - n_channels * sizeof(__le32)); + if (!resp_cp) { + resp_cp = ERR_PTR(-ENOMEM); + goto exit; } - } - if (!resp_cp) { - ret = -ENOMEM; - goto exit; + resp_cp->status = mcc_resp_v1->status; + resp_cp->mcc = mcc_resp_v1->mcc; + resp_cp->cap = mcc_resp_v1->cap; + resp_cp->source_id = mcc_resp_v1->source_id; + resp_cp->n_channels = mcc_resp_v1->n_channels; + memcpy(resp_cp->channels, mcc_resp_v1->channels, + n_channels * sizeof(__le32)); } status = le32_to_cpu(resp_cp->status); @@ -824,8 +827,6 @@ iwl_mvm_update_mcc(struct iwl_mvm *mvm, const char *alpha2, exit: iwl_free_resp(&cmd); - if (ret) - return ERR_PTR(ret); return resp_cp; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 1da55e4a1048..2e4bfe9f07ec 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -849,7 +849,7 @@ static void iwl_op_mode_mvm_stop(struct iwl_op_mode *op_mode) iwl_phy_db_free(mvm->phy_db); mvm->phy_db = NULL; - iwl_free_nvm_data(mvm->nvm_data); + kfree(mvm->nvm_data); for (i = 0; i < NVM_MAX_NUM_SECTIONS; i++) kfree(mvm->nvm_sections[i].data); @@ -1094,6 +1094,16 @@ static void iwl_mvm_wake_sw_queue(struct iwl_op_mode *op_mode, int hw_queue) iwl_mvm_start_mac_queues(mvm, mq); } +static void iwl_mvm_set_rfkill_state(struct iwl_mvm *mvm) +{ + bool state = iwl_mvm_is_radio_killed(mvm); + + if (state) + wake_up(&mvm->rx_sync_waitq); + + wiphy_rfkill_set_hw_state(mvm->hw->wiphy, state); +} + void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) { if (state) @@ -1101,7 +1111,7 @@ void iwl_mvm_set_hw_ctkill_state(struct iwl_mvm *mvm, bool state) else clear_bit(IWL_MVM_STATUS_HW_CTKILL, &mvm->status); - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); + iwl_mvm_set_rfkill_state(mvm); } static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) @@ -1114,7 +1124,7 @@ static bool iwl_mvm_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) else clear_bit(IWL_MVM_STATUS_HW_RFKILL, &mvm->status); - wiphy_rfkill_set_hw_state(mvm->hw->wiphy, iwl_mvm_is_radio_killed(mvm)); + iwl_mvm_set_rfkill_state(mvm); /* iwl_run_init_mvm_ucode is waiting for results, abort it */ if (calibrating) @@ -1171,9 +1181,13 @@ static void iwl_mvm_fw_error_dump_wk(struct work_struct *work) /* start recording again if the firmware is not crashed */ if (!test_bit(STATUS_FW_ERROR, &mvm->trans->status) && - mvm->fw->dbg_dest_tlv) + mvm->fw->dbg_dest_tlv) { iwl_clear_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x100); + iwl_clear_bits_prph(mvm->trans, + MON_BUFF_SAMPLE_CTL, 0x1); + iwl_set_bits_prph(mvm->trans, MON_BUFF_SAMPLE_CTL, 0x1); + } } else { u32 in_sample = iwl_read_prph(mvm->trans, DBGC_IN_SAMPLE); u32 out_ctrl = iwl_read_prph(mvm->trans, DBGC_OUT_CTRL); @@ -1313,7 +1327,7 @@ static bool iwl_mvm_disallow_offloading(struct iwl_mvm *mvm, * for offloading in order to prevent reuse of the same * qos seq counters. */ - if (iwl_mvm_tid_queued(tid_data)) + if (iwl_mvm_tid_queued(mvm, tid_data)) continue; if (tid_data->state != IWL_AGG_OFF) @@ -1463,9 +1477,15 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) synchronize_net(); /* Flush the hw queues, in case something got queued during entry */ - ret = iwl_mvm_flush_tx_path(mvm, iwl_mvm_flushable_queues(mvm), flags); - if (ret) - return ret; + /* TODO new tx api */ + if (iwl_mvm_has_new_tx_api(mvm)) { + WARN_ONCE(1, "d0i3: Need to implement flush TX queue\n"); + } else { + ret = iwl_mvm_flush_tx_path(mvm, iwl_mvm_flushable_queues(mvm), + flags); + if (ret) + return ret; + } /* configure wowlan configuration only if needed */ if (mvm->d0i3_ap_sta_id != IWL_MVM_INVALID_STA) { @@ -1611,9 +1631,6 @@ static void iwl_mvm_d0i3_exit_work(struct work_struct *wk) if (ret) goto out; - if (!get_status_cmd.resp_pkt) - goto out; - status = (void *)get_status_cmd.resp_pkt->data; wakeup_reasons = le32_to_cpu(status->wakeup_reasons); qos_seq = status->qos_seq_ctr; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index aa785cf3cf68..a02dda8d9ea3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2836,7 +2836,11 @@ static void rs_initialize_lq(struct iwl_mvm *mvm, rs_get_initial_rate(mvm, sta, lq_sta, band, rate); rs_init_optimal_rate(mvm, sta, lq_sta); - WARN_ON_ONCE(rate->ant != ANT_A && rate->ant != ANT_B); + WARN_ONCE(rate->ant != ANT_A && rate->ant != ANT_B, + "ant: 0x%x, chains 0x%x, fw tx ant: 0x%x, nvm tx ant: 0x%x\n", + rate->ant, lq_sta->pers.chains, mvm->fw->valid_tx_ant, + mvm->nvm_data ? mvm->nvm_data->valid_tx_ant : ANT_INVALID); + tbl->column = rs_get_column_from_rate(rate); rs_set_expected_tpt_table(lq_sta, tbl); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index cf48390f6f68..f3e608196369 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -502,7 +502,7 @@ void iwl_mvm_reorder_timer_expired(unsigned long data) buf->sta_id, sn); iwl_mvm_release_frames(buf->mvm, sta, NULL, buf, sn); rcu_read_unlock(); - } else if (buf->num_stored) { + } else { /* * If no frame expired and there are stored frames, index is now * pointing to the first unexpired frame - modify timer diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index bc52739eeb25..02f35a929606 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -734,7 +734,6 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, spin_lock_bh(&mvmsta->lock); mvmsta->tid_data[tid].txq_id = queue; mvmsta->tid_data[tid].is_tid_active = true; - mvmsta->tfd_queue_msk |= BIT(queue); spin_unlock_bh(&mvmsta->lock); return 0; @@ -758,7 +757,7 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, bool using_inactive_queue = false, same_sta = false; unsigned long disable_agg_tids = 0; enum iwl_mvm_agg_state queue_state; - bool shared_queue = false; + bool shared_queue = false, inc_ssn; int ssn; unsigned long tfd_queue_mask; int ret; @@ -885,8 +884,12 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, } ssn = IEEE80211_SEQ_TO_SN(le16_to_cpu(hdr->seq_ctrl)); - iwl_mvm_enable_txq(mvm, queue, mac_queue, ssn, &cfg, - wdg_timeout); + inc_ssn = iwl_mvm_enable_txq(mvm, queue, mac_queue, + ssn, &cfg, wdg_timeout); + if (inc_ssn) { + ssn = (ssn + 1) & IEEE80211_SCTL_SEQ; + le16_add_cpu(&hdr->seq_ctrl, 0x10); + } /* * Mark queue as shared in transport if shared @@ -898,6 +901,13 @@ static int iwl_mvm_sta_alloc_queue(struct iwl_mvm *mvm, iwl_trans_txq_set_shared_mode(mvm->trans, queue, true); spin_lock_bh(&mvmsta->lock); + /* + * This looks racy, but it is not. We have only one packet for + * this ra/tid in our Tx path since we stop the Qdisc when we + * need to allocate a new TFD queue. + */ + if (inc_ssn) + mvmsta->tid_data[tid].seq_number += 0x10; mvmsta->tid_data[tid].txq_id = queue; mvmsta->tid_data[tid].is_tid_active = true; mvmsta->tfd_queue_msk |= BIT(queue); @@ -1993,8 +2003,6 @@ int iwl_mvm_send_add_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) mvm->probe_queue = queue; else if (vif->type == NL80211_IFTYPE_P2P_DEVICE) mvm->p2p_dev_queue = queue; - - bsta->tfd_queue_msk |= BIT(queue); } return 0; @@ -2004,29 +2012,32 @@ static void iwl_mvm_free_bcast_sta_queues(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + int queue; lockdep_assert_held(&mvm->mutex); iwl_mvm_flush_sta(mvm, &mvmvif->bcast_sta, true, 0); - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) - iwl_mvm_disable_txq(mvm, vif->cab_queue, vif->cab_queue, - IWL_MAX_TID_COUNT, 0); - - if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->probe_queue)) { - iwl_mvm_disable_txq(mvm, mvm->probe_queue, - vif->hw_queue[0], IWL_MAX_TID_COUNT, - 0); - mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->probe_queue); + switch (vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + queue = mvm->probe_queue; + break; + case NL80211_IFTYPE_P2P_DEVICE: + queue = mvm->p2p_dev_queue; + break; + default: + WARN(1, "Can't free bcast queue on vif type %d\n", + vif->type); + return; } - if (mvmvif->bcast_sta.tfd_queue_msk & BIT(mvm->p2p_dev_queue)) { - iwl_mvm_disable_txq(mvm, mvm->p2p_dev_queue, - vif->hw_queue[0], IWL_MAX_TID_COUNT, - 0); - mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(mvm->p2p_dev_queue); - } + iwl_mvm_disable_txq(mvm, queue, vif->hw_queue[0], IWL_MAX_TID_COUNT, 0); + if (iwl_mvm_has_new_tx_api(mvm)) + return; + + WARN_ON(!(mvmvif->bcast_sta.tfd_queue_msk & BIT(queue))); + mvmvif->bcast_sta.tfd_queue_msk &= ~BIT(queue); } /* Send the FW a request to remove the station from it's internal data @@ -2529,6 +2540,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data; + u16 normalized_ssn; int txq_id; int ret; @@ -2616,7 +2628,15 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvmsta->sta_id, tid, txq_id, tid_data->ssn, tid_data->next_reclaimed); - if (tid_data->ssn == tid_data->next_reclaimed) { + /* + * In A000 HW, the next_reclaimed index is only 8 bit, so we'll need + * to align the wrap around of ssn so we compare relevant values. + */ + normalized_ssn = tid_data->ssn; + if (mvm->trans->cfg->gen2) + normalized_ssn &= 0xff; + + if (normalized_ssn == tid_data->next_reclaimed) { tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); } else { @@ -2814,8 +2834,13 @@ int iwl_mvm_sta_tx_agg_stop(struct iwl_mvm *mvm, struct ieee80211_vif *vif, "ssn = %d, next_recl = %d\n", tid_data->ssn, tid_data->next_reclaimed); - /* There are still packets for this RA / TID in the HW */ - if (tid_data->ssn != tid_data->next_reclaimed) { + /* + * There are still packets for this RA / TID in the HW. + * Not relevant for DQA mode, since there is no need to disable + * the queue. + */ + if (!iwl_mvm_is_dqa_supported(mvm) && + tid_data->ssn != tid_data->next_reclaimed) { tid_data->state = IWL_EMPTYING_HW_QUEUE_DELBA; err = 0; break; @@ -2888,14 +2913,17 @@ int iwl_mvm_sta_tx_agg_flush(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (old_state >= IWL_AGG_ON) { iwl_mvm_drain_sta(mvm, mvmsta, true); - if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) - IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); - if (iwl_mvm_has_new_tx_api(mvm)) + if (iwl_mvm_has_new_tx_api(mvm)) { + if (iwl_mvm_flush_sta_tids(mvm, mvmsta->sta_id, + BIT(tid), 0)) + IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); iwl_trans_wait_txq_empty(mvm->trans, txq_id); - - else + } else { + if (iwl_mvm_flush_tx_path(mvm, BIT(txq_id), 0)) + IWL_ERR(mvm, "Couldn't flush the AGG queue\n"); iwl_trans_wait_tx_queues_empty(mvm->trans, BIT(txq_id)); + } iwl_mvm_drain_sta(mvm, mvmsta, false); @@ -2975,7 +3003,7 @@ static struct iwl_mvm_sta *iwl_mvm_get_key_sta(struct iwl_mvm *mvm, } static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, - struct iwl_mvm_sta *mvm_sta, + u32 sta_id, struct ieee80211_key_conf *key, bool mcast, u32 tkip_iv32, u16 *tkip_p1k, u32 cmd_flags, u8 key_offset) @@ -2993,6 +3021,9 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, bool new_api = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TKIP_MIC_KEYS); + if (sta_id == IWL_MVM_INVALID_STA) + return -EINVAL; + keyidx = (key->keyidx << STA_KEY_FLG_KEYID_POS) & STA_KEY_FLG_KEYID_MSK; key_flags = cpu_to_le16(keyidx); @@ -3051,7 +3082,7 @@ static int iwl_mvm_send_sta_key(struct iwl_mvm *mvm, u.cmd.common.key_offset = key_offset; u.cmd.common.key_flags = key_flags; - u.cmd.common.sta_id = mvm_sta->sta_id; + u.cmd.common.sta_id = sta_id; if (new_api) { u.cmd.transmit_seq_cnt = cpu_to_le64(pn); @@ -3184,19 +3215,37 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, u8 key_offset, bool mcast) { - struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); int ret; const u8 *addr; struct ieee80211_key_seq seq; u16 p1k[5]; + u32 sta_id; + + if (sta) { + struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta); + + sta_id = mvm_sta->sta_id; + } else if (vif->type == NL80211_IFTYPE_AP && + !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + sta_id = mvmvif->mcast_sta.sta_id; + } else { + IWL_ERR(mvm, "Failed to find station id\n"); + return -EINVAL; + } switch (keyconf->cipher) { case WLAN_CIPHER_SUITE_TKIP: + if (vif->type == NL80211_IFTYPE_AP) { + ret = -EINVAL; + break; + } addr = iwl_mvm_get_mac_addr(mvm, vif, sta); /* get phase 1 key from mac80211 */ ieee80211_get_key_rx_seq(keyconf, 0, &seq); ieee80211_get_tkip_rx_p1k(keyconf, addr, seq.tkip.iv32, p1k); - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, seq.tkip.iv32, p1k, 0, key_offset); break; case WLAN_CIPHER_SUITE_CCMP: @@ -3204,11 +3253,11 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, case WLAN_CIPHER_SUITE_WEP104: case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, 0, NULL, 0, key_offset); break; default: - ret = iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + ret = iwl_mvm_send_sta_key(mvm, sta_id, keyconf, mcast, 0, NULL, 0, key_offset); } @@ -3229,6 +3278,9 @@ static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, int ret, size; u32 status; + if (sta_id == IWL_MVM_INVALID_STA) + return -EINVAL; + key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & STA_KEY_FLG_KEYID_MSK); key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); @@ -3272,42 +3324,48 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, { bool mcast = !(keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE); struct iwl_mvm_sta *mvm_sta; - u8 sta_id; + u8 sta_id = IWL_MVM_INVALID_STA; int ret; static const u8 __maybe_unused zero_addr[ETH_ALEN] = {0}; lockdep_assert_held(&mvm->mutex); - /* Get the station id from the mvm local station table */ - mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); - if (!mvm_sta) { - IWL_ERR(mvm, "Failed to find station\n"); - return -EINVAL; - } - sta_id = mvm_sta->sta_id; + if (vif->type != NL80211_IFTYPE_AP || + keyconf->flags & IEEE80211_KEY_FLAG_PAIRWISE) { + /* Get the station id from the mvm local station table */ + mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); + if (!mvm_sta) { + IWL_ERR(mvm, "Failed to find station\n"); + return -EINVAL; + } + sta_id = mvm_sta->sta_id; - if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || - keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || - keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) { - ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, false); - goto end; - } + if (keyconf->cipher == WLAN_CIPHER_SUITE_AES_CMAC || + keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_128 || + keyconf->cipher == WLAN_CIPHER_SUITE_BIP_GMAC_256) { + ret = iwl_mvm_send_sta_igtk(mvm, keyconf, sta_id, + false); + goto end; + } - /* - * It is possible that the 'sta' parameter is NULL, and thus - * there is a need to retrieve the sta from the local station table. - */ - if (!sta) { - sta = rcu_dereference_protected(mvm->fw_id_to_mac_id[sta_id], - lockdep_is_held(&mvm->mutex)); - if (IS_ERR_OR_NULL(sta)) { - IWL_ERR(mvm, "Invalid station id\n"); - return -EINVAL; + /* + * It is possible that the 'sta' parameter is NULL, and thus + * there is a need to retrieve the sta from the local station + * table. + */ + if (!sta) { + sta = rcu_dereference_protected( + mvm->fw_id_to_mac_id[sta_id], + lockdep_is_held(&mvm->mutex)); + if (IS_ERR_OR_NULL(sta)) { + IWL_ERR(mvm, "Invalid station id\n"); + return -EINVAL; + } } - } - if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) - return -EINVAL; + if (WARN_ON_ONCE(iwl_mvm_sta_from_mac80211(sta)->vif != vif)) + return -EINVAL; + } /* If the key_offset is not pre-assigned, we need to find a * new offset to use. In normal cases, the offset is not @@ -3337,8 +3395,9 @@ int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, * to the same key slot (offset). * If this fails, remove the original as well. */ - if (keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || - keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) { + if ((keyconf->cipher == WLAN_CIPHER_SUITE_WEP40 || + keyconf->cipher == WLAN_CIPHER_SUITE_WEP104) && + sta) { ret = __iwl_mvm_set_sta_key(mvm, vif, sta, keyconf, key_offset, !mcast); if (ret) { @@ -3372,6 +3431,9 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); if (mvm_sta) sta_id = mvm_sta->sta_id; + else if (!sta && vif->type == NL80211_IFTYPE_AP && mcast) + sta_id = iwl_mvm_vif_from_mac80211(vif)->mcast_sta.sta_id; + IWL_DEBUG_WEP(mvm, "mvm remove dynamic key: idx=%d sta=%d\n", keyconf->keyidx, sta_id); @@ -3394,7 +3456,7 @@ int iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, } mvm->fw_key_deleted[keyconf->hw_key_idx] = 0; - if (!mvm_sta) { + if (sta && !mvm_sta) { IWL_DEBUG_WEP(mvm, "station non-existent, early return.\n"); return 0; } @@ -3425,7 +3487,7 @@ void iwl_mvm_update_tkip_key(struct iwl_mvm *mvm, mvm_sta = iwl_mvm_get_key_sta(mvm, vif, sta); if (WARN_ON_ONCE(!mvm_sta)) goto unlock; - iwl_mvm_send_sta_key(mvm, mvm_sta, keyconf, mcast, + iwl_mvm_send_sta_key(mvm, mvm_sta->sta_id, keyconf, mcast, iv32, phase1key, CMD_ASYNC, keyconf->hw_key_idx); unlock: @@ -3501,7 +3563,7 @@ void iwl_mvm_sta_modify_sleep_tx_count(struct iwl_mvm *mvm, return; } - n_queued = iwl_mvm_tid_queued(tid_data); + n_queued = iwl_mvm_tid_queued(mvm, tid_data); if (n_queued > remaining) { more_data = true; remaining = 0; @@ -3683,3 +3745,17 @@ void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif) rcu_read_unlock(); } + +u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data) +{ + u16 sn = IEEE80211_SEQ_TO_SN(tid_data->seq_number); + + /* + * In A000 HW, the next_reclaimed index is only 8 bit, so we'll need + * to align the wrap around of ssn so we compare relevant values. + */ + if (mvm->trans->cfg->gen2) + sn &= 0xff; + + return ieee80211_sn_sub(sn, tid_data->next_reclaimed); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 357ff2bf75c1..05fecbe87da4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -341,12 +341,6 @@ struct iwl_mvm_tid_data { bool is_tid_active; }; -static inline u16 iwl_mvm_tid_queued(struct iwl_mvm_tid_data *tid_data) -{ - return ieee80211_sn_sub(IEEE80211_SEQ_TO_SN(tid_data->seq_number), - tid_data->next_reclaimed); -} - struct iwl_mvm_key_pn { struct rcu_head rcu_head; struct { @@ -447,6 +441,8 @@ struct iwl_mvm_sta { u8 avg_energy; }; +u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data); + static inline struct iwl_mvm_sta * iwl_mvm_sta_from_mac80211(struct ieee80211_sta *sta) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index aa3a3f336929..c89bb453c496 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -473,7 +473,10 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, if (unlikely(!dev_cmd)) return NULL; - memset(dev_cmd, 0, sizeof(*dev_cmd)); + /* Make sure we zero enough of dev_cmd */ + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) > sizeof(*tx_cmd)); + + memset(dev_cmd, 0, sizeof(dev_cmd->hdr) + sizeof(*tx_cmd)); dev_cmd->hdr.cmd = TX_CMD; if (iwl_mvm_has_new_tx_api(mvm)) { @@ -648,6 +651,9 @@ int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb) info.control.vif->type == NL80211_IFTYPE_STATION && queue != mvm->aux_queue) { queue = IWL_MVM_DQA_BSS_CLIENT_QUEUE; + } else if (iwl_mvm_is_dqa_supported(mvm) && + info.control.vif->type == NL80211_IFTYPE_MONITOR) { + queue = mvm->aux_queue; } } @@ -1126,13 +1132,14 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid]; struct ieee80211_vif *vif = mvmsta->vif; + u16 normalized_ssn; lockdep_assert_held(&mvmsta->lock); if ((tid_data->state == IWL_AGG_ON || tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA || iwl_mvm_is_dqa_supported(mvm)) && - iwl_mvm_tid_queued(tid_data) == 0) { + iwl_mvm_tid_queued(mvm, tid_data) == 0) { /* * Now that this aggregation or DQA queue is empty tell * mac80211 so it knows we no longer have frames buffered for @@ -1141,7 +1148,15 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, ieee80211_sta_set_buffered(sta, tid, false); } - if (tid_data->ssn != tid_data->next_reclaimed) + /* + * In A000 HW, the next_reclaimed index is only 8 bit, so we'll need + * to align the wrap around of ssn so we compare relevant values. + */ + normalized_ssn = tid_data->ssn; + if (mvm->trans->cfg->gen2) + normalized_ssn &= 0xff; + + if (normalized_ssn != tid_data->next_reclaimed) return; switch (tid_data->state) { @@ -1319,6 +1334,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, struct ieee80211_sta *sta; u16 sequence = le16_to_cpu(pkt->hdr.sequence); int txq_id = SEQ_TO_QUEUE(sequence); + /* struct iwl_mvm_tx_resp_v3 is almost the same */ struct iwl_mvm_tx_resp *tx_resp = (void *)pkt->data; int sta_id = IWL_MVM_TX_RES_GET_RA(tx_resp->ra_tid); int tid = IWL_MVM_TX_RES_GET_TID(tx_resp->ra_tid); @@ -1336,7 +1352,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, __skb_queue_head_init(&skbs); if (iwl_mvm_has_new_tx_api(mvm)) - txq_id = le16_to_cpu(tx_resp->v6.tx_queue); + txq_id = le16_to_cpu(tx_resp->tx_queue); seq_ctl = le16_to_cpu(tx_resp->seq_ctl); @@ -1485,7 +1501,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, if (mvmsta->sleep_tx_count) { mvmsta->sleep_tx_count--; if (mvmsta->sleep_tx_count && - !iwl_mvm_tid_queued(tid_data)) { + !iwl_mvm_tid_queued(mvm, tid_data)) { /* * The number of frames in the queue * dropped to 0 even if we sent less @@ -1889,11 +1905,13 @@ out: int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) { int ret; - struct iwl_tx_path_flush_cmd flush_cmd = { + struct iwl_tx_path_flush_cmd_v1 flush_cmd = { .queues_ctl = cpu_to_le32(tfd_msk), .flush_ctl = cpu_to_le16(DUMP_TX_FIFO_FLUSH), }; + WARN_ON(iwl_mvm_has_new_tx_api(mvm)); + ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, sizeof(flush_cmd), &flush_cmd); if (ret) @@ -1901,19 +1919,41 @@ int iwl_mvm_flush_tx_path(struct iwl_mvm *mvm, u32 tfd_msk, u32 flags) return ret; } -int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool int_sta, u32 flags) +int iwl_mvm_flush_sta_tids(struct iwl_mvm *mvm, u32 sta_id, + u16 tids, u32 flags) { - u32 mask; + int ret; + struct iwl_tx_path_flush_cmd flush_cmd = { + .sta_id = cpu_to_le32(sta_id), + .tid_mask = cpu_to_le16(tids), + }; - if (int_sta) { - struct iwl_mvm_int_sta *int_sta = sta; + WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); - mask = int_sta->tfd_queue_msk; - } else { - struct iwl_mvm_sta *mvm_sta = sta; + ret = iwl_mvm_send_cmd_pdu(mvm, TXPATH_FLUSH, flags, + sizeof(flush_cmd), &flush_cmd); + if (ret) + IWL_ERR(mvm, "Failed to send flush command (%d)\n", ret); + return ret; +} - mask = mvm_sta->tfd_queue_msk; +int iwl_mvm_flush_sta(struct iwl_mvm *mvm, void *sta, bool internal, u32 flags) +{ + struct iwl_mvm_int_sta *int_sta = sta; + struct iwl_mvm_sta *mvm_sta = sta; + + if (iwl_mvm_has_new_tx_api(mvm)) { + if (internal) + return iwl_mvm_flush_sta_tids(mvm, int_sta->sta_id, + BIT(IWL_MGMT_TID), flags); + + return iwl_mvm_flush_sta_tids(mvm, mvm_sta->sta_id, + 0xFF, flags); } - return iwl_mvm_flush_tx_path(mvm, mask, flags); + if (internal) + return iwl_mvm_flush_tx_path(mvm, int_sta->tfd_queue_msk, + flags); + + return iwl_mvm_flush_tx_path(mvm, mvm_sta->tfd_queue_msk, flags); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 8f4f176e204e..0093e78dd571 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -69,6 +69,7 @@ #include "iwl-debug.h" #include "iwl-io.h" #include "iwl-prph.h" +#include "iwl-csr.h" #include "fw-dbg.h" #include "mvm.h" #include "fw-api-rs.h" @@ -168,11 +169,6 @@ int iwl_mvm_send_cmd_status(struct iwl_mvm *mvm, struct iwl_host_cmd *cmd, } pkt = cmd->resp_pkt; - /* Can happen if RFKILL is asserted */ - if (!pkt) { - ret = 0; - goto out_free_resp; - } resp_len = iwl_rx_packet_payload_len(pkt); if (WARN_ON_ONCE(resp_len != sizeof(*resp))) { @@ -502,6 +498,7 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) { struct iwl_trans *trans = mvm->trans; struct iwl_error_event_table table; + u32 val; if (mvm->cur_ucode == IWL_UCODE_INIT) { if (!base) @@ -520,6 +517,36 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u32 base) return; } + /* check if there is a HW error */ + val = iwl_trans_read_mem32(trans, base); + if (((val & ~0xf) == 0xa5a5a5a0) || ((val & ~0xf) == 0x5a5a5a50)) { + int err; + + IWL_ERR(trans, "HW error, resetting before reading\n"); + + /* reset the device */ + iwl_set_bit(trans, CSR_RESET, CSR_RESET_REG_FLAG_SW_RESET); + usleep_range(1000, 2000); + + /* set INIT_DONE flag */ + iwl_set_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + + /* and wait for clock stabilization */ + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + udelay(2); + + err = iwl_poll_bit(trans, CSR_GP_CNTRL, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY, + 25000); + if (err < 0) { + IWL_DEBUG_INFO(trans, + "Failed to reset the card for the dump\n"); + return; + } + } + iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); if (ERROR_START_OFFSET <= table.valid * ERROR_ELEM_SIZE) { @@ -730,35 +757,39 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, int mac80211_queue, return queue; } -void iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, +bool iwl_mvm_enable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, u16 ssn, const struct iwl_trans_txq_scd_cfg *cfg, unsigned int wdg_timeout) { + struct iwl_scd_txq_cfg_cmd cmd = { + .scd_queue = queue, + .action = SCD_CFG_ENABLE_QUEUE, + .window = cfg->frame_limit, + .sta_id = cfg->sta_id, + .ssn = cpu_to_le16(ssn), + .tx_fifo = cfg->fifo, + .aggregate = cfg->aggregate, + .tid = cfg->tid, + }; + bool inc_ssn; + if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) - return; + return false; /* Send the enabling command if we need to */ - if (iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, - cfg->sta_id, cfg->tid)) { - struct iwl_scd_txq_cfg_cmd cmd = { - .scd_queue = queue, - .action = SCD_CFG_ENABLE_QUEUE, - .window = cfg->frame_limit, - .sta_id = cfg->sta_id, - .ssn = cpu_to_le16(ssn), - .tx_fifo = cfg->fifo, - .aggregate = cfg->aggregate, - .tid = cfg->tid, - }; - - iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, NULL, - wdg_timeout); - WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, - sizeof(struct iwl_scd_txq_cfg_cmd), - &cmd), - "Failed to configure queue %d on FIFO %d\n", queue, - cfg->fifo); - } + if (!iwl_mvm_update_txq_mapping(mvm, queue, mac80211_queue, + cfg->sta_id, cfg->tid)) + return false; + + inc_ssn = iwl_trans_txq_enable_cfg(mvm->trans, queue, ssn, + NULL, wdg_timeout); + if (inc_ssn) + le16_add_cpu(&cmd.ssn, 1); + + WARN(iwl_mvm_send_cmd_pdu(mvm, SCD_QUEUE_CFG, 0, sizeof(cmd), &cmd), + "Failed to configure queue %d on FIFO %d\n", queue, cfg->fifo); + + return inc_ssn; } int iwl_mvm_disable_txq(struct iwl_mvm *mvm, int queue, int mac80211_queue, @@ -1186,7 +1217,11 @@ static void iwl_mvm_remove_inactive_tids(struct iwl_mvm *mvm, /* Go over all non-active TIDs, incl. IWL_MAX_TID_COUNT (for mgmt) */ for_each_set_bit(tid, &tid_bitmap, IWL_MAX_TID_COUNT + 1) { /* If some TFDs are still queued - don't mark TID as inactive */ - if (iwl_mvm_tid_queued(&mvmsta->tid_data[tid])) + if (iwl_mvm_tid_queued(mvm, &mvmsta->tid_data[tid])) + tid_bitmap &= ~BIT(tid); + + /* Don't mark as inactive any TID that has an active BA */ + if (mvmsta->tid_data[tid].state != IWL_AGG_OFF) tid_bitmap &= ~BIT(tid); } |